diff --git a/src/config.rs b/src/config.rs index ad23f7a77..a60007cb2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -45,6 +45,8 @@ pub enum Mode { Csv, /// Generate a JSON report for N cycles. Json, + /// Generate a Graphviz DOT file for N cycles. + Dot, /// Do not generate any tracing output for N cycles. Silent, } @@ -498,7 +500,7 @@ 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::Silent => { + Mode::Pretty | Mode::Markdown | Mode::Csv | Mode::Json | Mode::Dot | Mode::Silent => { Some(report_cycles) } }; diff --git a/src/main.rs b/src/main.rs index eca0b70c7..f525ba88d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -208,6 +208,7 @@ fn run_frontend( Mode::Json => report::run_report_json(&traces[0], args.report_cycles, &resolver)?, 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::Silent => report::run_report_silent(&traces[0], args.report_cycles)?, } Ok(()) diff --git a/src/report.rs b/src/report.rs index b7902482f..3ce8be6e6 100644 --- a/src/report.rs +++ b/src/report.rs @@ -1,3 +1,4 @@ +use crate::backend::flows::FlowEntry; use crate::backend::trace::Trace; use crate::TraceInfo; use anyhow::anyhow; @@ -5,7 +6,10 @@ use comfy_table::presets::{ASCII_MARKDOWN, UTF8_FULL}; use comfy_table::{ContentArrangement, Table}; use itertools::Itertools; use parking_lot::RwLock; +use petgraph::dot::{Config, Dot}; +use petgraph::graphmap::DiGraphMap; use serde::{Serialize, Serializer}; +use std::net::{IpAddr, Ipv4Addr}; use std::sync::Arc; use std::thread::sleep; use std::time::Duration; @@ -272,6 +276,31 @@ pub fn run_report_silent(info: &TraceInfo, report_cycles: usize) -> anyhow::Resu Ok(()) } +/// Run a trace and generate a dot file. +pub fn run_report_dot(info: &TraceInfo, report_cycles: usize) -> anyhow::Result<()> { + wait_for_round(&info.data, report_cycles)?; + let trace = info.data.read().clone(); + let mut graph: DiGraphMap = DiGraphMap::new(); + for (flow, _id) in trace.flow_registry().flows() { + for (fst, snd) in flow.entries.windows(2).map(|pair| (pair[0], pair[1])) { + match (fst, snd) { + (FlowEntry::Known(addr1), FlowEntry::Known(addr2)) => { + graph.add_edge(addr1, addr2, ()); + } + (FlowEntry::Known(addr1), FlowEntry::Unknown) => { + graph.add_edge(addr1, IpAddr::V4(Ipv4Addr::UNSPECIFIED), ()); + } + (FlowEntry::Unknown, FlowEntry::Known(addr2)) => { + graph.add_edge(IpAddr::V4(Ipv4Addr::UNSPECIFIED), addr2, ()); + } + _ => {} + } + } + } + println!("{:?}", Dot::with_config(&graph, &[Config::EdgeNoLabel])); + Ok(()) +} + /// Block until trace data for round `round` is available. fn wait_for_round(trace_data: &Arc>, report_cycles: usize) -> anyhow::Result { let mut trace = trace_data.read().clone(); diff --git a/trippy-config-sample.toml b/trippy-config-sample.toml index eaf55fe28..099c8e42d 100644 --- a/trippy-config-sample.toml +++ b/trippy-config-sample.toml @@ -29,6 +29,7 @@ # markdown - Generate a markdown text table report for N cycles # 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 # silent - Do not generate any output for N cycles mode = "tui"