retina_core/port/
statistics.rs1use super::PortId;
2use crate::dpdk;
3
4use indexmap::IndexMap;
5use std::ffi::CStr;
6use std::mem;
7
8use anyhow::{bail, Result};
9use colored::*;
10use prettytable::{color, format, Attr, Cell, Row, Table};
11
12#[derive(Debug)]
14pub(crate) struct PortStats {
15 pub(crate) stats: IndexMap<String, u64>,
16 pub(crate) port_id: PortId,
17}
18
19impl PortStats {
20 pub(crate) fn collect(port_id: PortId) -> Result<Self> {
22 let mut table: Vec<dpdk::rte_eth_xstat> = vec![];
24 let len = unsafe { dpdk::rte_eth_xstats_get(port_id.raw(), table.as_mut_ptr(), 0) };
25 if len < 0 {
26 bail!("Invalid Port ID: {}", port_id);
27 }
28
29 let mut labels = Vec::with_capacity(len as usize);
30 for _ in 0..len {
31 let xstat_name: dpdk::rte_eth_xstat_name = unsafe { mem::zeroed() };
32 labels.push(xstat_name);
33 }
34
35 let nb_labels = unsafe {
36 dpdk::rte_eth_xstats_get_names(port_id.raw(), labels.as_mut_ptr(), len as u32)
37 };
38 if nb_labels < 0 || nb_labels > len {
39 bail!("Failed to retrieve port statistics labels.");
40 }
41
42 let mut xstats = Vec::with_capacity(len as usize);
43 for _ in 0..len {
44 let xstat: dpdk::rte_eth_xstat = unsafe { mem::zeroed() };
45 xstats.push(xstat);
46 }
47 let nb_xstats =
48 unsafe { dpdk::rte_eth_xstats_get(port_id.raw(), xstats.as_mut_ptr(), len as u32) };
49 if nb_xstats < 0 || nb_xstats > len {
50 bail!("Failed to retrieve port statistics.");
51 }
52
53 if nb_labels != nb_xstats {
54 bail!("Number of labels does not match number of retrieved statistics.");
55 }
56
57 let mut stats = IndexMap::new();
58 for i in 0..nb_xstats {
59 let label = unsafe { CStr::from_ptr(labels[i as usize].name.as_ptr()) };
60 let value = xstats[i as usize].value;
61 stats.insert(label.to_string_lossy().into_owned(), value);
62 }
63 Ok(PortStats { stats, port_id })
64 }
65
66 pub(crate) fn display(&self, keywords: &[String]) {
68 if keywords.is_empty() {
69 return;
70 }
71 println!("Port {} statistics", self.port_id);
72 self.display_capture_rate();
73 self.display_out_of_buffer_rate();
74 self.display_discard_rate();
75
76 let mut table = Table::new();
77 table.set_format(*format::consts::FORMAT_NO_LINESEP);
78 for (label, value) in self.stats.iter() {
79 if keywords.iter().any(|k| label.contains(k)) {
80 let value_cell = if *value > 0
81 && (label.contains("error")
82 || label.contains("discard")
83 || label.contains("out_of_buffer"))
84 {
85 Cell::new_align(&value.to_string(), format::Alignment::RIGHT)
86 .with_style(Attr::ForegroundColor(color::RED))
87 } else {
88 Cell::new_align(&value.to_string(), format::Alignment::RIGHT)
89 };
90
91 table.add_row(Row::new(vec![value_cell, Cell::new(label)]));
92 }
93 }
94 table.printstd();
95 }
96
97 pub(super) fn display_capture_rate(&self) {
104 let captured = self.stats.get("rx_good_packets");
105 let total = self.stats.get("rx_phy_packets");
106
107 match (captured, total) {
108 (Some(captured), Some(total)) => {
109 let capture_rate = *captured as f64 / *total as f64;
110 println!("SW Capture %: {}", capture_rate.to_string().bright_cyan());
111 }
112 _ => println!("SW Capture %: UNKNOWN"),
113 }
114 }
115
116 pub(super) fn display_out_of_buffer_rate(&self) {
121 let discards = self.stats.get("rx_out_of_buffer");
122 let total = self.stats.get("rx_phy_packets");
123
124 match (discards, total) {
125 (Some(discards), Some(total)) => {
126 let discard_rate = *discards as f64 / *total as f64;
127
128 if discard_rate > 0.0001 {
130 println!("Out of Buffer %: {}", discard_rate.to_string().bright_red());
131 } else if discard_rate > 0.0 {
132 println!(
133 "Out of Buffer %: {}",
134 discard_rate.to_string().bright_yellow()
135 );
136 } else {
137 println!(
138 "Out of Buffer %: {}",
139 discard_rate.to_string().bright_green()
140 );
141 }
142 }
143 _ => println!("Out of Buffer %: UNKNOWN"),
144 }
145 }
146
147 pub(super) fn display_discard_rate(&self) {
152 let discards = self.stats.get("rx_phy_discard_packets");
153 let total = self.stats.get("rx_phy_packets");
154
155 match (discards, total) {
156 (Some(discards), Some(total)) => {
157 let discard_rate = *discards as f64 / *total as f64;
158
159 if discard_rate > 0.0001 {
161 println!("HW Discard %: {}", discard_rate.to_string().bright_red());
162 } else if discard_rate > 0.0 {
163 println!("HW Discard %: {}", discard_rate.to_string().bright_yellow());
164 } else {
165 println!("HW Discard %: {}", discard_rate.to_string().bright_green());
166 }
167 }
168 _ => println!("HW Discard %: UNKNOWN"),
169 }
170 }
171}