Skip to content

Commit

Permalink
feat(tui): display individual tracing flows in Tui (#777)
Browse files Browse the repository at this point in the history
  • Loading branch information
fujiapple852 committed Nov 25, 2023
1 parent 1d022a4 commit 1590f47
Show file tree
Hide file tree
Showing 18 changed files with 310 additions and 33 deletions.
26 changes: 24 additions & 2 deletions src/backend/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ use trippy::tracing::{Extensions, Probe, ProbeStatus, TracerRound};
#[derive(Debug, Clone)]
pub struct Trace {
max_samples: usize,
/// The flow id for the current round.
round_flow_id: FlowId,
/// Tracing data per registered flow id.
trace_data: HashMap<FlowId, TraceData>,
/// Flow registry.
registry: FlowRegistry,
/// Tracing error message.
error: Option<String>,
}

Expand All @@ -22,6 +27,7 @@ impl Trace {
Self {
trace_data: once((Self::default_flow_id(), TraceData::new(max_samples)))
.collect::<HashMap<FlowId, TraceData>>(),
round_flow_id: Self::default_flow_id(),
max_samples,
registry: FlowRegistry::new(),
error: None,
Expand Down Expand Up @@ -68,6 +74,11 @@ impl Trace {
self.trace_data[&flow_id].round_count()
}

/// The `FlowId` for the current round.
pub fn round_flow_id(&self) -> FlowId {
self.round_flow_id
}

/// The registry of flows in the trace.
pub fn flows(&self) -> &[(Flow, FlowId)] {
self.registry.flows()
Expand All @@ -94,6 +105,7 @@ impl Trace {
.map(|p| p.host),
);
let flow_id = self.registry.register(flow);
self.round_flow_id = flow_id;
self.update_trace_flow(Self::default_flow_id(), round);
self.update_trace_flow(flow_id, round);
}
Expand All @@ -110,18 +122,28 @@ impl Trace {
/// Information about a single `Hop` within a `Trace`.
#[derive(Debug, Clone)]
pub struct Hop {
/// The ttl of this hop.
ttl: u8,
/// The addrs of this hop and associated counts.
addrs: IndexMap<IpAddr, usize>,
/// The total probes sent for this hop.
total_sent: usize,
/// The total probes received for this hop.
total_recv: usize,
/// The total round trip time for this hop across all rounds.
total_time: Duration,
/// The round trip time for this hop in the current round.
last: Option<Duration>,
/// The best round trip time for this hop across all rounds.
best: Option<Duration>,
/// The worst round trip time for this hop across all rounds.
worst: Option<Duration>,
mean: f64,
m2: f64,
/// The history of round trip times across the last N rounds.
samples: Vec<Duration>,
/// The ICMP extensions for this hop.
extensions: Option<Extensions>,
mean: f64,
m2: f64,
}

impl Hop {
Expand Down
9 changes: 9 additions & 0 deletions src/config/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub struct TuiBindings {
pub toggle_freeze: TuiKeyBinding,
pub toggle_chart: TuiKeyBinding,
pub toggle_map: TuiKeyBinding,
pub toggle_flows: TuiKeyBinding,
pub expand_hosts: TuiKeyBinding,
pub contract_hosts: TuiKeyBinding,
pub expand_hosts_max: TuiKeyBinding,
Expand Down Expand Up @@ -60,6 +61,7 @@ impl Default for TuiBindings {
),
toggle_chart: TuiKeyBinding::new(KeyCode::Char('c')),
toggle_map: TuiKeyBinding::new(KeyCode::Char('m')),
toggle_flows: TuiKeyBinding::new(KeyCode::Char('f')),
expand_hosts: TuiKeyBinding::new(KeyCode::Char(']')),
contract_hosts: TuiKeyBinding::new(KeyCode::Char('[')),
expand_hosts_max: TuiKeyBinding::new(KeyCode::Char('}')),
Expand Down Expand Up @@ -106,6 +108,7 @@ impl TuiBindings {
(self.toggle_freeze, TuiCommandItem::ToggleFreeze),
(self.toggle_chart, TuiCommandItem::ToggleChart),
(self.toggle_map, TuiCommandItem::ToggleMap),
(self.toggle_flows, TuiCommandItem::ToggleFlows),
(self.expand_hosts, TuiCommandItem::ExpandHosts),
(self.expand_hosts_max, TuiCommandItem::ExpandHostsMax),
(self.contract_hosts, TuiCommandItem::ContractHosts),
Expand Down Expand Up @@ -201,6 +204,10 @@ impl From<(HashMap<TuiCommandItem, TuiKeyBinding>, ConfigBindings)> for TuiBindi
.get(&TuiCommandItem::ToggleChart)
.or(cfg.toggle_chart.as_ref())
.unwrap_or(&Self::default().toggle_chart),
toggle_flows: *cmd_items
.get(&TuiCommandItem::ToggleFlows)
.or(cfg.toggle_flows.as_ref())
.unwrap_or(&TuiKeyBinding::new(KeyCode::Char('f'))),
toggle_map: *cmd_items
.get(&TuiCommandItem::ToggleMap)
.or(cfg.toggle_map.as_ref())
Expand Down Expand Up @@ -506,6 +513,8 @@ pub enum TuiCommandItem {
ToggleChart,
/// Toggle the map.
ToggleMap,
/// Toggle the flows panel.
ToggleFlows,
/// Expand hosts.
ExpandHosts,
/// Expand hosts to max.
Expand Down
10 changes: 10 additions & 0 deletions src/config/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ pub struct ConfigThemeColors {
pub hops_chart_axis_color: Option<TuiColor>,
pub frequency_chart_bar_color: Option<TuiColor>,
pub frequency_chart_text_color: Option<TuiColor>,
pub flows_chart_bar_selected_color: Option<TuiColor>,
pub flows_chart_bar_unselected_color: Option<TuiColor>,
pub flows_chart_text_current_color: Option<TuiColor>,
pub flows_chart_text_non_current_color: Option<TuiColor>,
pub samples_chart_color: Option<TuiColor>,
pub help_dialog_bg_color: Option<TuiColor>,
pub help_dialog_text_color: Option<TuiColor>,
Expand Down Expand Up @@ -288,6 +292,10 @@ impl Default for ConfigThemeColors {
hops_chart_axis_color: Some(theme.hops_chart_axis_color),
frequency_chart_bar_color: Some(theme.frequency_chart_bar_color),
frequency_chart_text_color: Some(theme.frequency_chart_text_color),
flows_chart_bar_selected_color: Some(theme.flows_chart_bar_selected_color),
flows_chart_bar_unselected_color: Some(theme.flows_chart_bar_unselected_color),
flows_chart_text_current_color: Some(theme.flows_chart_text_current_color),
flows_chart_text_non_current_color: Some(theme.flows_chart_text_non_current_color),
samples_chart_color: Some(theme.samples_chart_color),
help_dialog_bg_color: Some(theme.help_dialog_bg_color),
help_dialog_text_color: Some(theme.help_dialog_text_color),
Expand Down Expand Up @@ -323,6 +331,7 @@ pub struct ConfigBindings {
pub address_mode_both: Option<TuiKeyBinding>,
pub toggle_freeze: Option<TuiKeyBinding>,
pub toggle_chart: Option<TuiKeyBinding>,
pub toggle_flows: Option<TuiKeyBinding>,
pub toggle_map: Option<TuiKeyBinding>,
pub expand_hosts: Option<TuiKeyBinding>,
pub contract_hosts: Option<TuiKeyBinding>,
Expand Down Expand Up @@ -356,6 +365,7 @@ impl Default for ConfigBindings {
address_mode_both: Some(bindings.address_mode_both),
toggle_freeze: Some(bindings.toggle_freeze),
toggle_chart: Some(bindings.toggle_chart),
toggle_flows: Some(bindings.toggle_flows),
toggle_map: Some(bindings.toggle_map),
expand_hosts: Some(bindings.expand_hosts),
contract_hosts: Some(bindings.contract_hosts),
Expand Down
36 changes: 36 additions & 0 deletions src/config/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ pub struct TuiTheme {
pub frequency_chart_bar_color: TuiColor,
/// The color of text in the bars of the frequency chart.
pub frequency_chart_text_color: TuiColor,
/// The color of the selected flow bar in the flows chart.
pub flows_chart_bar_selected_color: TuiColor,
/// The color of the unselected flow bar in the flows chart.
pub flows_chart_bar_unselected_color: TuiColor,
/// The color of the current flow text in the flows chart.
pub flows_chart_text_current_color: TuiColor,
/// The color of the non-current flow text in the flows chart.
pub flows_chart_text_non_current_color: TuiColor,
/// The color of the samples chart.
pub samples_chart_color: TuiColor,
/// The background color of the help dialog.
Expand Down Expand Up @@ -85,6 +93,10 @@ impl Default for TuiTheme {
hops_chart_axis_color: TuiColor::DarkGray,
frequency_chart_bar_color: TuiColor::Green,
frequency_chart_text_color: TuiColor::Gray,
flows_chart_bar_selected_color: TuiColor::Green,
flows_chart_bar_unselected_color: TuiColor::DarkGray,
flows_chart_text_current_color: TuiColor::LightGreen,
flows_chart_text_non_current_color: TuiColor::White,
samples_chart_color: TuiColor::Yellow,
help_dialog_bg_color: TuiColor::Blue,
help_dialog_text_color: TuiColor::Gray,
Expand Down Expand Up @@ -160,6 +172,22 @@ impl From<(HashMap<TuiThemeItem, TuiColor>, ConfigThemeColors)> for TuiTheme {
.get(&TuiThemeItem::FrequencyChartTextColor)
.or(cfg.frequency_chart_text_color.as_ref())
.unwrap_or(&Self::default().frequency_chart_text_color),
flows_chart_bar_selected_color: *color_map
.get(&TuiThemeItem::FlowsChartBarSelectedColor)
.or(cfg.flows_chart_bar_selected_color.as_ref())
.unwrap_or(&Self::default().flows_chart_bar_selected_color),
flows_chart_bar_unselected_color: *color_map
.get(&TuiThemeItem::FlowsChartBarUnselectedColor)
.or(cfg.flows_chart_bar_unselected_color.as_ref())
.unwrap_or(&Self::default().flows_chart_bar_unselected_color),
flows_chart_text_current_color: *color_map
.get(&TuiThemeItem::FlowsChartTextCurrentColor)
.or(cfg.flows_chart_text_current_color.as_ref())
.unwrap_or(&Self::default().flows_chart_text_current_color),
flows_chart_text_non_current_color: *color_map
.get(&TuiThemeItem::FlowsChartTextNonCurrentColor)
.or(cfg.flows_chart_text_non_current_color.as_ref())
.unwrap_or(&Self::default().flows_chart_text_non_current_color),
samples_chart_color: *color_map
.get(&TuiThemeItem::SamplesChartColor)
.or(cfg.samples_chart_color.as_ref())
Expand Down Expand Up @@ -251,6 +279,14 @@ pub enum TuiThemeItem {
FrequencyChartBarColor,
/// The color of text in the bars of the frequency chart.
FrequencyChartTextColor,
/// The color of the selected flow bar in the flows chart.
FlowsChartBarSelectedColor,
/// The color of the unselected flow bar in the flows chart.
FlowsChartBarUnselectedColor,
/// The color of the current flow text in the flows chart.
FlowsChartTextCurrentColor,
/// The color of the non-current flow text in the flows chart.
FlowsChartTextNonCurrentColor,
/// The color of the samples chart.
SamplesChartColor,
/// The background color of the help dialog.
Expand Down
16 changes: 14 additions & 2 deletions src/frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub fn run_frontend(
Ok(())
}

#[allow(clippy::too_many_lines)]
fn run_app<B: Backend>(
terminal: &mut Terminal<B>,
trace_info: Vec<TraceInfo>,
Expand All @@ -66,6 +67,7 @@ fn run_app<B: Backend>(
if app.frozen_start.is_none() {
app.snapshot_trace_data();
app.clamp_selected_hop();
app.update_order_flow_counts();
};
terminal.draw(|f| render::app::render(f, &mut app))?;
if event::poll(app.tui_config.refresh_rate)? {
Expand Down Expand Up @@ -105,9 +107,17 @@ fn run_app<B: Backend>(
} else if bindings.previous_hop.check(key) {
app.previous_hop();
} else if bindings.previous_trace.check(key) {
app.previous_trace();
if app.show_flows {
app.previous_flow();
} else {
app.previous_trace();
}
} else if bindings.next_trace.check(key) {
app.next_trace();
if app.show_flows {
app.next_flow();
} else {
app.next_trace();
}
} else if bindings.next_hop_address.check(key) {
app.next_hop_address();
} else if bindings.previous_hop_address.check(key) {
Expand All @@ -124,6 +134,8 @@ fn run_app<B: Backend>(
app.toggle_chart();
} else if bindings.toggle_map.check(key) {
app.toggle_map();
} else if bindings.toggle_flows.check(key) {
app.toggle_flows();
} else if bindings.contract_hosts_min.check(key) {
app.contract_hosts_min();
} else if bindings.expand_hosts_max.check(key) {
Expand Down
2 changes: 2 additions & 0 deletions src/frontend/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct Bindings {
pub toggle_freeze: KeyBinding,
pub toggle_chart: KeyBinding,
pub toggle_map: KeyBinding,
pub toggle_flows: KeyBinding,
pub expand_hosts: KeyBinding,
pub contract_hosts: KeyBinding,
pub expand_hosts_max: KeyBinding,
Expand Down Expand Up @@ -53,6 +54,7 @@ impl From<TuiBindings> for Bindings {
toggle_freeze: KeyBinding::from(value.toggle_freeze),
toggle_chart: KeyBinding::from(value.toggle_chart),
toggle_map: KeyBinding::from(value.toggle_map),
toggle_flows: KeyBinding::from(value.toggle_flows),
expand_hosts: KeyBinding::from(value.expand_hosts),
contract_hosts: KeyBinding::from(value.contract_hosts),
expand_hosts_max: KeyBinding::from(value.expand_hosts_max),
Expand Down
1 change: 1 addition & 0 deletions src/frontend/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod app;
pub mod body;
pub mod bsod;
pub mod chart;
pub mod flows;
pub mod footer;
pub mod header;
pub mod help;
Expand Down
22 changes: 19 additions & 3 deletions src/frontend/render/app.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::frontend::render::{body, footer, header, help, settings, tabs};
use crate::frontend::render::{body, flows, footer, header, help, settings, tabs};
use crate::frontend::tui_app::TuiApp;
use ratatui::layout::{Constraint, Direction, Layout};
use ratatui::Frame;
Expand All @@ -10,7 +10,9 @@ use ratatui::Frame;
/// ____________________________________
/// | Header |
/// ------------------------------------
/// | Tabs |
/// | Tabs |
/// ------------------------------------
/// | Flows |
/// ------------------------------------
/// | |
/// | |
Expand All @@ -25,7 +27,8 @@ use ratatui::Frame;
/// ------------------------------------
///
/// Header - the title, configuration, destination, clock and keyboard controls
/// Tab - a tab for each target being traced (only shown if > 1 target requested)
/// Tab - a tab for each target being traced (shown if > 1 target requested, can't be used with flows)
/// Flows - a navigable chart of individual trace flows (toggled on/off, can't be used with tabs)
/// Hops - a table where each row represents a single hop (time-to-live) in the trace
/// History - a graph of historic round-trip ping samples for the target host
/// Frequency - a histogram of sample frequencies by round-trip time for the target host
Expand All @@ -35,6 +38,8 @@ use ratatui::Frame;
pub fn render(f: &mut Frame<'_>, app: &mut TuiApp) {
let constraints = if app.trace_info.len() > 1 {
LAYOUT_WITH_TABS.as_slice()
} else if app.show_flows {
LAYOUT_WITH_FLOWS.as_slice()
} else {
LAYOUT_WITHOUT_TABS.as_slice()
};
Expand All @@ -47,6 +52,10 @@ pub fn render(f: &mut Frame<'_>, app: &mut TuiApp) {
tabs::render(f, chunks[1], app);
body::render(f, chunks[2], app);
footer::render(f, chunks[3], app);
} else if app.show_flows {
flows::render(f, chunks[1], app);
body::render(f, chunks[2], app);
footer::render(f, chunks[3], app);
} else {
body::render(f, chunks[1], app);
footer::render(f, chunks[2], app);
Expand All @@ -70,3 +79,10 @@ const LAYOUT_WITH_TABS: [Constraint; 4] = [
Constraint::Min(10),
Constraint::Length(6),
];

const LAYOUT_WITH_FLOWS: [Constraint; 4] = [
Constraint::Length(5),
Constraint::Length(6),
Constraint::Min(10),
Constraint::Length(6),
];
3 changes: 1 addition & 2 deletions src/frontend/render/chart.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::backend::trace::Trace;
use crate::frontend::tui_app::TuiApp;
use ratatui::layout::{Alignment, Constraint, Rect};
use ratatui::style::Style;
Expand All @@ -13,7 +12,7 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
let samples = app.tui_config.max_samples / app.zoom_factor;
let series_data = app
.selected_tracer_data
.hops(Trace::default_flow_id())
.hops(app.selected_flow)
.iter()
.map(|hop| {
hop.samples()
Expand Down
Loading

0 comments on commit 1590f47

Please sign in to comment.