Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions examples/socket_analysis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
use pree::socket::monitor::{SocketEvent, SocketMonitor};
use pree::socket::platform::{CongestionControl, CongestionState, SocketStats};
use std::thread;
use std::time::Duration;

fn main() -> pree::Result<()> {
// Create a socket monitor with a shorter interval for more frequent updates
let mut monitor = SocketMonitor::new().interval(Duration::from_millis(500));

// Register callbacks for socket events with analysis
monitor.on_socket_change(|event| match event {
SocketEvent::Opened(socket) => {
println!("New socket opened:");
print_socket_info(&socket);
if let Some(stats) = &socket.stats {
analyze_socket_stats(stats);
}
println!();
}
SocketEvent::Closed(socket) => {
println!("Socket closed:");
print_socket_info(&socket);
if let Some(stats) = &socket.stats {
analyze_socket_stats(stats);
}
println!();
}
SocketEvent::StateChanged(socket) => {
println!("Socket state changed:");
print_socket_info(&socket);
if let Some(stats) = &socket.stats {
analyze_socket_stats(stats);
}
println!();
}
})?;

// Start monitoring
monitor.start()?;

// Monitor for 30 seconds
thread::sleep(Duration::from_secs(30));

// Stop monitoring
monitor.stop();

Ok(())
}

fn print_socket_info(socket: &pree::socket::platform::SocketInfo) {
println!(" Protocol: {}", socket.protocol);
println!(" Local: {}", socket.local_addr);
println!(" Remote: {}", socket.remote_addr);
println!(" State: {:?}", socket.state);
if let Some(pid) = socket.process_id {
println!(
" Process: {} (PID: {})",
socket.process_name.as_deref().unwrap_or("unknown"),
pid
);
}
}

fn analyze_socket_stats(stats: &SocketStats) {
println!(" Connection Analysis:");

// Basic metrics
println!(
" Bytes sent/received: {}/{}",
stats.bytes_sent, stats.bytes_received
);
println!(
" Packets sent/received: {}/{}",
stats.packets_sent, stats.packets_received
);
println!(" Retransmits: {}", stats.retransmits);

// RTT analysis
if let Some(rtt) = stats.rtt {
println!(" RTT: {:.2}ms", rtt.as_secs_f32() * 1000.0);
if let Some(rtt_var) = stats.rtt_variance {
println!(" RTT variance: {:.2}ms", rtt_var.as_secs_f32() * 1000.0);
}
}

// Congestion control analysis
if let Some(cc) = &stats.congestion_control {
println!(" Congestion Control: {cc:?}");
match cc {
CongestionControl::Cubic => {
println!(" Using CUBIC algorithm for high-speed networks");
}
CongestionControl::Bbr => {
println!(" Using BBR for better throughput and latency");
}
CongestionControl::Reno => {
println!(" Using Reno for basic congestion control");
}
CongestionControl::Vegas => {
println!(" Using Vegas for proactive congestion avoidance");
}
CongestionControl::Westwood => {
println!(" Using Westwood for wireless networks");
}
CongestionControl::Other(s) => println!(" Using custom algorithm: {s}"),
}
}
if let Some(state) = &stats.congestion_state {
println!(" Congestion State: {state:?}");
match state {
CongestionState::SlowStart => {
println!(" In slow start phase - growing window exponentially");
}
CongestionState::CongestionAvoidance => {
println!(" In congestion avoidance - growing window linearly");
}
CongestionState::FastRecovery => {
println!(" In fast recovery - recovering from packet loss");
}
CongestionState::FastRetransmit => {
println!(" In fast retransmit - retransmitting lost packets");
}
CongestionState::Other(s) => println!(" In custom state: {s}"),
}
}
if stats.is_congested() {
println!(" ⚠️ Connection is experiencing congestion");
}

// Connection quality analysis
let quality_score = stats.calculate_quality_score();
println!(
" Connection Quality Score: {:.1}%",
quality_score * 100.0
);

if let Some(util) = stats.bandwidth_utilization() {
println!(" Bandwidth Utilization: {util:.1}%");
}
Comment on lines +137 to +139
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify bandwidth_utilization() method handles edge cases properly.

The code assumes bandwidth_utilization() returns a valid percentage, but should verify the method handles cases where bandwidth calculation might fail or return invalid values.


🏁 Script executed:

#!/bin/bash
# Description: Check if bandwidth_utilization method has proper error handling
# Expected: Method should handle edge cases like zero bandwidth or division by zero

ast-grep --pattern $'impl SocketStats {
  $$$
  fn bandwidth_utilization($$$) -> Option<$_> {
    $$$
  }
  $$$
}'

Length of output: 115


🏁 Script executed:

#!/bin/bash
# Locate the SocketStats implementation and the bandwidth_utilization method
rg -n "impl SocketStats" -R .
rg -n "fn bandwidth_utilization" -R .

Length of output: 881


🏁 Script executed:

#!/bin/bash
# Locate and display the bandwidth_utilization implementation
rg -n 'fn bandwidth_utilization' -g '*.rs' .
rg -n -C5 'fn bandwidth_utilization' -g '*.rs' .

Length of output: 936


🏁 Script executed:

#!/bin/bash
# Display the full bandwidth_utilization implementation for review
sed -n '220,280p' src/socket/platform.rs

Length of output: 1955


Guard against zero connection_duration to avoid division by zero

The current bandwidth_utilization() checks rtt_secs > 0.0 but still divides by self.connection_duration?.as_secs_f32() without verifying that the duration is non-zero. If connection_duration is zero, this yields Inf/NaN which then gets clamped to 100%, masking the invalid case.

• File: src/socket/platform.rs, method bandwidth_utilization
• Add a check for duration.as_secs_f32() > 0.0 (similar to the rtt_secs guard), returning None when it’s zero

Suggested diff:

     pub fn bandwidth_utilization(&self) -> Option<f32> {
         if let (Some(cwnd), Some(mss)) = (self.congestion_window, self.snd_mss) {
             if let Some(rtt) = self.rtt {
                 let rtt_secs = rtt.as_secs_f32();
                 if rtt_secs > 0.0 {
-                    let max_bandwidth = (cwnd * mss) as f32 / rtt_secs;
-                    let actual_bandwidth =
-                        self.bytes_sent as f32 / self.connection_duration?.as_secs_f32();
-                    Some((actual_bandwidth / max_bandwidth * 100.0).min(100.0))
+                    let max_bandwidth = (cwnd * mss) as f32 / rtt_secs;
+                    // Ensure we don't divide by zero on connection_duration
+                    if let Some(duration) = self.connection_duration {
+                        let duration_secs = duration.as_secs_f32();
+                        if duration_secs > 0.0 {
+                            let actual_bandwidth = self.bytes_sent as f32 / duration_secs;
+                            return Some((actual_bandwidth / max_bandwidth * 100.0).min(100.0));
+                        }
+                    }
+                    None
                 } else {
                     None
                 }
             } else {
                 None
             }
         } else {
             None
         }
     }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/socket/platform.rs around the bandwidth_utilization method, add a check
to ensure that connection_duration.as_secs_f32() is greater than 0.0 before
performing the division. If the duration is zero, return None to avoid division
by zero and prevent returning invalid bandwidth utilization values. This will
improve the method's robustness by properly handling edge cases where connection
duration might be zero.


let loss_rate = stats.packet_loss_rate();
if loss_rate > 0.0 {
println!(" Packet Loss Rate: {loss_rate:.1}%");
}

if stats.has_buffer_bloat() {
println!(" ⚠️ Buffer bloat detected");
}

// SACK and ECN analysis
if stats.sack_enabled {
println!(" SACK enabled");
if let Some(blocks) = stats.sack_blocks {
println!(" SACK blocks: {blocks}");
}
if let Some(reordering) = stats.sack_reordering {
println!(" SACK reordering events: {reordering}");
}
}

if stats.ecn_enabled {
println!(" ECN enabled");
if let Some(ce_count) = stats.ecn_ce_count {
println!(" ECN Congestion Experienced count: {ce_count}");
}
}

// Window analysis
if let (Some(send_win), Some(recv_win)) = (stats.send_window, stats.receive_window) {
println!(" Send/Receive Window: {send_win}/{recv_win}");
}
if let Some(zero_win) = stats.zero_window_events {
if zero_win > 0 {
println!(" ⚠️ Zero window events: {zero_win}");
}
}

// Connection duration
if let Some(duration) = stats.connection_duration {
println!(" Connection Duration: {:.1}s", duration.as_secs_f32());
}
}
26 changes: 25 additions & 1 deletion src/socket/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

use crate::socket::platform::SocketInfo;
use crate::socket::platform::{SocketFamily, SocketInfo, SocketType};
use crate::socket::socket::Socket;
use crate::types::{Protocol, SocketState};
use crate::Result;
Expand Down Expand Up @@ -126,6 +126,14 @@ impl SocketMonitor {
process_id: socket.process_id,
process_name: socket.process_name.clone(),
stats: None,
socket_type: match socket.protocol {
Protocol::Tcp => Some(SocketType::Stream),
Protocol::Udp => Some(SocketType::Datagram),
_ => None,
},
socket_family: Some(SocketFamily::Inet),
socket_flags: None,
socket_options: None,
Comment on lines +129 to +136
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Extract socket metadata creation to reduce code duplication.

The socket metadata assignment logic is duplicated three times (here, lines 168-175, and 210-217). This violates the DRY principle and makes maintenance harder.

Consider extracting this logic into a helper function:

+fn create_socket_metadata(protocol: Protocol) -> (Option<SocketType>, Option<SocketFamily>, Option<SocketFlags>, Option<SocketOptions>) {
+    let socket_type = match protocol {
+        Protocol::Tcp => Some(SocketType::Stream),
+        Protocol::Udp => Some(SocketType::Datagram),
+        _ => None,
+    };
+    (socket_type, Some(SocketFamily::Inet), None, None)
+}

Then use it in all three locations:

+                                let (socket_type, socket_family, socket_flags, socket_options) = create_socket_metadata(socket.protocol);
                                socket_type: match socket.protocol {
                                    Protocol::Tcp => Some(SocketType::Stream),
                                    Protocol::Udp => Some(SocketType::Datagram),
                                    _ => None,
                                },
-                                socket_family: Some(SocketFamily::Inet),
-                                socket_flags: None,
-                                socket_options: None,
+                                socket_type,
+                                socket_family,
+                                socket_flags,
+                                socket_options,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
socket_type: match socket.protocol {
Protocol::Tcp => Some(SocketType::Stream),
Protocol::Udp => Some(SocketType::Datagram),
_ => None,
},
socket_family: Some(SocketFamily::Inet),
socket_flags: None,
socket_options: None,
let (socket_type, socket_family, socket_flags, socket_options) =
create_socket_metadata(socket.protocol);
socket_type,
socket_family,
socket_flags,
socket_options,
🤖 Prompt for AI Agents
In src/socket/monitor.rs around lines 129 to 136, the socket metadata creation
code is duplicated in three places, violating the DRY principle. Extract this
socket metadata assignment logic into a separate helper function that returns
the constructed metadata struct. Replace the duplicated code in all three
locations with calls to this new helper function to centralize and simplify
maintenance.

};
let _ = tx.send(SocketEvent::Opened(info));
}
Expand Down Expand Up @@ -157,6 +165,14 @@ impl SocketMonitor {
process_id: socket.process_id,
process_name: socket.process_name.clone(),
stats: None,
socket_type: match socket.protocol {
Protocol::Tcp => Some(SocketType::Stream),
Protocol::Udp => Some(SocketType::Datagram),
_ => None,
},
socket_family: Some(SocketFamily::Inet),
socket_flags: None,
socket_options: None,
};
let _ = tx.send(SocketEvent::Closed(info));
}
Expand Down Expand Up @@ -191,6 +207,14 @@ impl SocketMonitor {
process_id: socket.process_id,
process_name: socket.process_name.clone(),
stats: None,
socket_type: match socket.protocol {
Protocol::Tcp => Some(SocketType::Stream),
Protocol::Udp => Some(SocketType::Datagram),
_ => None,
},
socket_family: Some(SocketFamily::Inet),
socket_flags: None,
socket_options: None,
};
let _ = tx.send(SocketEvent::StateChanged(info));
}
Expand Down
Loading
Loading