in src/main.rs [308:530]
fn render_graphs(f: &mut Frame, app: &mut App, area: Rect) {
let data_buf = app.data_buf.lock().unwrap();
let mut cpu_data: Vec<(f64, f64)> = vec![(0.0, 0.0); data_buf.len()];
let mut eps_data: Vec<(f64, f64)> = vec![(0.0, 0.0); data_buf.len()];
let mut runtime_data: Vec<(f64, f64)> = vec![(0.0, 0.0); data_buf.len()];
let mut total_cpu = 0.0;
let mut total_eps = 0;
let mut total_runtime = 0;
let mut moving_max_cpu = 0.0;
let mut moving_max_eps = 0;
let mut moving_max_runtime = 0;
for (i, val) in data_buf.iter().enumerate() {
cpu_data[i] = (i as f64, val.cpu_time_percent);
eps_data[i] = (i as f64, val.events_per_sec as f64);
runtime_data[i] = (i as f64, val.average_runtime_ns as f64);
if val.cpu_time_percent > app.max_cpu {
app.max_cpu = val.cpu_time_percent;
}
if val.cpu_time_percent > moving_max_cpu {
moving_max_cpu = val.cpu_time_percent;
}
if val.events_per_sec > app.max_eps {
app.max_eps = val.events_per_sec;
}
if val.events_per_sec > moving_max_eps {
moving_max_eps = val.events_per_sec;
}
if val.average_runtime_ns > app.max_runtime {
app.max_runtime = val.average_runtime_ns;
}
if val.average_runtime_ns > moving_max_runtime {
moving_max_runtime = val.average_runtime_ns;
}
total_cpu += val.cpu_time_percent;
total_eps += val.events_per_sec;
total_runtime += val.average_runtime_ns;
}
let max_cpu = moving_max_cpu;
let max_eps = moving_max_eps as f64;
let max_runtime = moving_max_runtime as f64;
let mut avg_cpu = 0.0;
let mut avg_eps = 0.0;
let mut avg_runtime = 0.0;
if data_buf.len() > 0 {
avg_cpu = total_cpu / data_buf.len() as f64;
avg_eps = total_eps as f64 / data_buf.len() as f64;
avg_runtime = total_runtime as f64 / data_buf.len() as f64;
}
let cpu_y_max = app.max_cpu.ceil();
let eps_y_max = (app.max_eps as f64 * 2.0).ceil();
let runtime_y_max = (app.max_runtime as f64 * 2.0).ceil();
// CPU
let cpu_dataset = Dataset::default()
.marker(symbols::Marker::Braille)
.graph_type(GraphType::Line)
.style(Style::default().green())
.data(&cpu_data);
let cpu_datasets = vec![cpu_dataset];
let x_axis = Axis::default()
.style(Style::default())
.bounds([0.0, cpu_data.len() as f64]);
let y_axis = Axis::default()
.style(Style::default())
.bounds([0.0, cpu_y_max])
.labels(vec![
"0%".into(),
((cpu_y_max / 2.0).to_string() + "%"),
(cpu_y_max.to_string() + "%"),
]);
let cpu_chart = Chart::new(cpu_datasets)
.block(
Block::default()
.title(format!(
" Total CPU % | Moving Avg: {} | Max: {} ",
format_percent(avg_cpu),
format_percent(max_cpu)
))
.borders(Borders::ALL),
)
.x_axis(x_axis)
.y_axis(y_axis);
// Events per second
let eps_dataset = Dataset::default()
.marker(symbols::Marker::Braille)
.graph_type(GraphType::Line)
.style(Style::default().cyan())
.data(&eps_data);
let eps_datasets = vec![eps_dataset];
let x_axis = Axis::default()
.style(Style::default())
.bounds([0.0, eps_data.len() as f64]);
let y_axis = Axis::default()
.style(Style::default())
.bounds([0.0, eps_y_max])
.labels(vec![
"0".into(),
((eps_y_max / 2.0).to_string()),
(eps_y_max.to_string()),
]);
let eps_chart = Chart::new(eps_datasets)
.block(
Block::default()
.title(format!(
" Events per second | Moving Avg: {} | Max: {} ",
avg_eps.ceil(),
max_eps.ceil()
))
.borders(Borders::ALL),
)
.x_axis(x_axis)
.y_axis(y_axis);
// Runtime
let runtime_dataset = Dataset::default()
.marker(symbols::Marker::Braille)
.graph_type(GraphType::Line)
.style(Style::default().magenta())
.data(&runtime_data);
let runtime_datasets = vec![runtime_dataset];
let x_axis = Axis::default()
.style(Style::default())
.bounds([0.0, runtime_data.len() as f64]);
let y_axis = Axis::default()
.style(Style::default())
.bounds([0.0, runtime_y_max])
.labels(vec![
"0".into(),
((runtime_y_max / 2.0).to_string()),
(runtime_y_max.to_string()),
]);
let runtime_chart = Chart::new(runtime_datasets)
.block(
Block::default()
.title(format!(
" Avg Runtime (ns) | Moving Avg: {} | Max: {} ",
avg_runtime.ceil(),
max_runtime.ceil()
))
.borders(Borders::ALL),
)
.x_axis(x_axis)
.y_axis(y_axis);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(area);
let sub_chunks = chunks
.iter()
.map(|chunk| {
Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(*chunk)
})
.collect::<Vec<_>>();
let mut items = vec![
Row::new(vec![Cell::from("Program ID"), Cell::from("Unknown")]),
Row::new(vec![Cell::from("Program Type"), Cell::from("Unknown")]),
Row::new(vec![Cell::from("Program Name"), Cell::from("Unknown")]),
];
let widths = [Constraint::Length(15), Constraint::Min(0)];
if let Some(bpf_program) = app.graphs_bpf_program.lock().unwrap().clone() {
items = vec![
Row::new(vec![
Cell::from("Program ID".bold()),
Cell::from(bpf_program.id.to_string()),
])
.height(2),
Row::new(vec![
Cell::from("Program Type".bold()),
Cell::from(bpf_program.bpf_type),
])
.height(2),
Row::new(vec![
Cell::from("Program Name".bold()),
Cell::from(bpf_program.name),
])
.height(2),
Row::new(vec![
Cell::from("PIDs".bold()),
Cell::from(
bpf_program
.processes
.iter()
.map(|pid| pid.to_string())
.collect::<Vec<String>>()
.join(", "),
),
])
.height(2),
];
}
let table = Table::new(items, widths)
.block(
Block::default()
.title(" Program Information ")
.padding(Padding::new(3, 0, 1, 0))
.borders(Borders::ALL),
)
.style(Style::default());
f.render_widget(table, sub_chunks[0][0]); // Top left
f.render_widget(cpu_chart.clone(), sub_chunks[0][1]); // Top right
f.render_widget(eps_chart, sub_chunks[1][0]); // Bottom left
f.render_widget(runtime_chart, sub_chunks[1][1]); // Bottom right
}