Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add flows report mode #779

Merged
merged 3 commits into from
Nov 11, 2023
Merged
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
94 changes: 59 additions & 35 deletions src/backend/flows.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use derive_more::{Add, AddAssign, Sub, SubAssign};
use itertools::{EitherOrBoth, Itertools};
use std::fmt::{Debug, Display, Formatter};
use std::net::IpAddr;

/// Identifies a tracing `Flow`.
Expand All @@ -20,6 +21,12 @@ use std::net::IpAddr;
)]
pub struct FlowId(pub u64);

impl Display for FlowId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

/// A register of tracing `Flows`.
#[derive(Debug, Clone, Default)]
pub struct FlowRegistry {
Expand All @@ -34,7 +41,7 @@ impl FlowRegistry {
pub fn new() -> Self {
Self {
flows: Vec::new(),
next_flow_id: FlowId(0),
next_flow_id: FlowId(1),
}
}

Expand Down Expand Up @@ -152,6 +159,12 @@ impl Flow {
}
}

impl Display for Flow {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.entries.iter().format(", "))
}
}

/// The result of a `Flow` comparison check.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum CheckStatus {
Expand All @@ -172,6 +185,17 @@ pub enum FlowEntry {
Known(IpAddr),
}

impl Display for FlowEntry {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unknown => f.write_str("*"),
Self::Known(addr) => {
write!(f, "{addr}")
}
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -183,9 +207,9 @@ mod tests {
let mut registry = FlowRegistry::new();
let flow1 = Flow::from_hops([addr("1.1.1.1")]);
let flow_id = registry.register(flow1);
assert_eq!(FlowId(0), flow_id);
assert_eq!(FlowId(1), flow_id);
assert_eq!(
&[(Flow::from_hops([addr("1.1.1.1")]), FlowId(0))],
&[(Flow::from_hops([addr("1.1.1.1")]), FlowId(1))],
registry.flows()
);
}
Expand All @@ -197,8 +221,8 @@ mod tests {
let flow1_id = registry.register(flow1.clone());
let flow2 = Flow::from_hops([addr("2.2.2.2")]);
let flow2_id = registry.register(flow2.clone());
assert_eq!(FlowId(0), flow1_id);
assert_eq!(FlowId(1), flow2_id);
assert_eq!(FlowId(1), flow1_id);
assert_eq!(FlowId(2), flow2_id);
assert_eq!(&[(flow1, flow1_id), (flow2, flow2_id)], registry.flows());
}

Expand All @@ -209,8 +233,8 @@ mod tests {
let flow1_id = registry.register(flow1.clone());
let flow2 = Flow::from_hops([addr("1.1.1.1")]);
let flow2_id = registry.register(flow2);
assert_eq!(FlowId(0), flow1_id);
assert_eq!(FlowId(0), flow2_id);
assert_eq!(FlowId(1), flow1_id);
assert_eq!(FlowId(1), flow2_id);
assert_eq!(&[(flow1, flow1_id)], registry.flows());
}

Expand All @@ -223,9 +247,9 @@ mod tests {
let flow2_id = registry.register(flow2.clone());
let flow3 = Flow::from_hops([addr("1.1.1.1")]);
let flow3_id = registry.register(flow3);
assert_eq!(FlowId(0), flow1_id);
assert_eq!(FlowId(1), flow2_id);
assert_eq!(FlowId(0), flow3_id);
assert_eq!(FlowId(1), flow1_id);
assert_eq!(FlowId(2), flow2_id);
assert_eq!(FlowId(1), flow3_id);
assert_eq!(&[(flow1, flow1_id), (flow2, flow2_id)], registry.flows());
}

Expand All @@ -242,11 +266,11 @@ mod tests {
let flow4_id = registry.register(flow4);
let flow5 = Flow::from_hops([addr("1.1.1.1")]);
let flow5_id = registry.register(flow5);
assert_eq!(FlowId(0), flow1_id);
assert_eq!(FlowId(0), flow2_id);
assert_eq!(FlowId(0), flow3_id);
assert_eq!(FlowId(1), flow4_id);
assert_eq!(FlowId(0), flow5_id);
assert_eq!(FlowId(1), flow1_id);
assert_eq!(FlowId(1), flow2_id);
assert_eq!(FlowId(1), flow3_id);
assert_eq!(FlowId(2), flow4_id);
assert_eq!(FlowId(1), flow5_id);
}

#[test]
Expand All @@ -260,10 +284,10 @@ mod tests {
let flow3_id = registry.register(flow3);
let flow4 = Flow::from_hops([addr("1.1.1.1"), addr("2.2.2.2"), addr("3.3.3.3")]);
let flow4_id = registry.register(flow4);
assert_eq!(FlowId(0), flow1_id);
assert_eq!(FlowId(0), flow2_id);
assert_eq!(FlowId(0), flow3_id);
assert_eq!(FlowId(0), flow4_id);
assert_eq!(FlowId(1), flow1_id);
assert_eq!(FlowId(1), flow2_id);
assert_eq!(FlowId(1), flow3_id);
assert_eq!(FlowId(1), flow4_id);
}

#[test]
Expand All @@ -286,10 +310,10 @@ mod tests {
// still matches flow 1
let flow4 = Flow::from_hops([addr("2.2.2.2")]);
let flow4_id = registry.register(flow4);
assert_eq!(FlowId(0), flow1_id);
assert_eq!(FlowId(1), flow2_id);
assert_eq!(FlowId(0), flow3_id);
assert_eq!(FlowId(1), flow4_id);
assert_eq!(FlowId(1), flow1_id);
assert_eq!(FlowId(2), flow2_id);
assert_eq!(FlowId(1), flow3_id);
assert_eq!(FlowId(2), flow4_id);
}

#[test]
Expand All @@ -299,8 +323,8 @@ mod tests {
let flow1_id = registry.register(flow1);
let flow2 = Flow::from_hops([addr("1.1.1.1")]);
let flow2_id = registry.register(flow2);
assert_eq!(FlowId(0), flow1_id);
assert_eq!(FlowId(0), flow2_id);
assert_eq!(FlowId(1), flow1_id);
assert_eq!(FlowId(1), flow2_id);
}

#[test]
Expand All @@ -310,8 +334,8 @@ mod tests {
let flow1_id = registry.register(flow1);
let flow2 = Flow::from_hops([addr("1.1.1.1"), None]);
let flow2_id = registry.register(flow2);
assert_eq!(FlowId(0), flow1_id);
assert_eq!(FlowId(0), flow2_id);
assert_eq!(FlowId(1), flow1_id);
assert_eq!(FlowId(1), flow2_id);
}

#[test]
Expand All @@ -321,8 +345,8 @@ mod tests {
let flow1_id = registry.register(flow1);
let flow2 = Flow::from_hops([addr("1.1.1.1"), addr("2.2.2.2")]);
let flow2_id = registry.register(flow2);
assert_eq!(FlowId(0), flow1_id);
assert_eq!(FlowId(0), flow2_id);
assert_eq!(FlowId(1), flow1_id);
assert_eq!(FlowId(1), flow2_id);
}

#[test]
Expand All @@ -332,8 +356,8 @@ mod tests {
let flow1_id = registry.register(flow1);
let flow2 = Flow::from_hops([addr("1.1.1.1"), addr("2.2.2.2")]);
let flow2_id = registry.register(flow2);
assert_eq!(FlowId(0), flow1_id);
assert_eq!(FlowId(0), flow2_id);
assert_eq!(FlowId(1), flow1_id);
assert_eq!(FlowId(1), flow2_id);
}

#[test]
Expand All @@ -343,8 +367,8 @@ mod tests {
let flow1_id = registry.register(flow1);
let flow2 = Flow::from_hops([None, addr("1.1.1.1")]);
let flow2_id = registry.register(flow2);
assert_eq!(FlowId(0), flow1_id);
assert_eq!(FlowId(0), flow2_id);
assert_eq!(FlowId(1), flow1_id);
assert_eq!(FlowId(1), flow2_id);
}

#[test]
Expand All @@ -354,8 +378,8 @@ mod tests {
let flow1_id = registry.register(flow1);
let flow2 = Flow::from_hops([None, addr("2.2.2.2")]);
let flow2_id = registry.register(flow2);
assert_eq!(FlowId(0), flow1_id);
assert_eq!(FlowId(1), flow2_id);
assert_eq!(FlowId(1), flow1_id);
assert_eq!(FlowId(2), flow2_id);
}

#[allow(clippy::unnecessary_wraps)]
Expand Down
12 changes: 9 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ pub enum Mode {
Json,
/// Generate a Graphviz DOT file for N cycles.
Dot,
/// Display all flows.
Flows,
/// Do not generate any tracing output for N cycles.
Silent,
}
Expand Down Expand Up @@ -500,9 +502,13 @@ impl TryFrom<(Args, &Platform)> for TrippyConfig {
let dns_timeout = humantime::parse_duration(&dns_timeout)?;
let max_rounds = match mode {
Mode::Stream | Mode::Tui => None,
Mode::Pretty | Mode::Markdown | Mode::Csv | Mode::Json | Mode::Dot | Mode::Silent => {
Some(report_cycles)
}
Mode::Pretty
| Mode::Markdown
| Mode::Csv
| Mode::Json
| Mode::Dot
| Mode::Flows
| Mode::Silent => Some(report_cycles),
};
let tui_max_addrs = match tui_max_addrs {
Some(n) if n > 0 => Some(n),
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ fn run_frontend(
Mode::Pretty => report::run_report_table_pretty(&traces[0], args.report_cycles, &resolver)?,
Mode::Markdown => report::run_report_table_md(&traces[0], args.report_cycles, &resolver)?,
Mode::Dot => report::run_report_dot(&traces[0], args.report_cycles)?,
Mode::Flows => report::run_report_flows(&traces[0], args.report_cycles)?,
Mode::Silent => report::run_report_silent(&traces[0], args.report_cycles)?,
}
Ok(())
Expand Down
10 changes: 10 additions & 0 deletions src/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,16 @@ pub fn run_report_dot(info: &TraceInfo, report_cycles: usize) -> anyhow::Result<
Ok(())
}

/// Run a trace and report all flows observed.
pub fn run_report_flows(info: &TraceInfo, report_cycles: usize) -> anyhow::Result<()> {
wait_for_round(&info.data, report_cycles)?;
let trace = info.data.read().clone();
for (flow, flow_id) in trace.flows() {
println!("flow {flow_id}: {flow}");
}
Ok(())
}

/// Block until trace data for round `round` is available.
fn wait_for_round(trace_data: &Arc<RwLock<Trace>>, report_cycles: usize) -> anyhow::Result<Trace> {
let mut trace = trace_data.read().clone();
Expand Down
1 change: 1 addition & 0 deletions trippy-config-sample.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
# csv - Generate a CSV report for N cycles
# json - Generate a JSON report for N cycles
# dot - Generate a Graphviz DOT report for N cycles
# flows - Display all flows
# silent - Do not generate any output for N cycles
mode = "tui"

Expand Down