Skip to content

Commit d37c318

Browse files
Add graphviz debug output for generic dataflow
1 parent 3e88aa2 commit d37c318

File tree

2 files changed

+510
-5
lines changed

2 files changed

+510
-5
lines changed

src/librustc_mir/dataflow/generic.rs

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,24 @@
1616
//! [gk]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
1717
//! [#64566]: https://github.com/rust-lang/rust/pull/64566
1818
19+
use std::borrow::Borrow;
1920
use std::cmp::Ordering;
20-
use std::ops;
21+
use std::ffi::OsString;
22+
use std::path::{Path, PathBuf};
23+
use std::{fs, io, ops};
2124

25+
use rustc::hir::def_id::DefId;
2226
use rustc::mir::{self, traversal, BasicBlock, Location};
27+
use rustc::ty::{self, TyCtxt};
28+
use rustc_data_structures::work_queue::WorkQueue;
2329
use rustc_index::bit_set::BitSet;
2430
use rustc_index::vec::{Idx, IndexVec};
25-
use rustc_data_structures::work_queue::WorkQueue;
31+
use syntax::symbol::sym;
2632

2733
use crate::dataflow::BottomValue;
2834

35+
mod graphviz;
36+
2937
/// A specific kind of dataflow analysis.
3038
///
3139
/// To run a dataflow analysis, one must set the initial state of the `START_BLOCK` via
@@ -62,6 +70,13 @@ pub trait Analysis<'tcx>: BottomValue {
6270
/// and try to keep it short.
6371
const NAME: &'static str;
6472

73+
/// How each element of your dataflow state will be displayed during debugging.
74+
///
75+
/// By default, this is the `fmt::Debug` representation of `Self::Idx`.
76+
fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> {
77+
write!(w, "{:?}", idx)
78+
}
79+
6580
/// The size of each bitvector allocated for each block.
6681
fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize;
6782

@@ -357,7 +372,9 @@ where
357372
{
358373
analysis: A,
359374
bits_per_block: usize,
375+
tcx: TyCtxt<'tcx>,
360376
body: &'a mir::Body<'tcx>,
377+
def_id: DefId,
361378
dead_unwinds: &'a BitSet<BasicBlock>,
362379
entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
363380
}
@@ -367,7 +384,9 @@ where
367384
A: Analysis<'tcx>,
368385
{
369386
pub fn new(
387+
tcx: TyCtxt<'tcx>,
370388
body: &'a mir::Body<'tcx>,
389+
def_id: DefId,
371390
dead_unwinds: &'a BitSet<BasicBlock>,
372391
analysis: A,
373392
) -> Self {
@@ -385,7 +404,9 @@ where
385404
Engine {
386405
analysis,
387406
bits_per_block,
407+
tcx,
388408
body,
409+
def_id,
389410
dead_unwinds,
390411
entry_sets,
391412
}
@@ -421,10 +442,26 @@ where
421442
);
422443
}
423444

424-
Results {
425-
analysis: self.analysis,
426-
entry_sets: self.entry_sets,
445+
let Engine {
446+
tcx,
447+
body,
448+
def_id,
449+
analysis,
450+
entry_sets,
451+
..
452+
} = self;
453+
454+
let results = Results { analysis, entry_sets };
455+
456+
let attrs = tcx.get_attrs(def_id);
457+
if let Some(path) = get_dataflow_graphviz_output_path(tcx, attrs, A::NAME) {
458+
let result = write_dataflow_graphviz_results(body, def_id, &path, &results);
459+
if let Err(e) = result {
460+
warn!("Failed to write dataflow results to {}: {}", path.display(), e);
461+
}
427462
}
463+
464+
results
428465
}
429466

430467
fn propagate_bits_into_graph_successors_of(
@@ -518,3 +555,59 @@ where
518555
}
519556
}
520557
}
558+
559+
/// Looks for attributes like `#[rustc_mir(borrowck_graphviz_postflow="./path/to/suffix.dot")]` and
560+
/// extracts the path with the given analysis name prepended to the suffix.
561+
///
562+
/// Returns `None` if no such attribute exists.
563+
fn get_dataflow_graphviz_output_path(
564+
tcx: TyCtxt<'tcx>,
565+
attrs: ty::Attributes<'tcx>,
566+
analysis: &str,
567+
) -> Option<PathBuf> {
568+
let mut rustc_mir_attrs = attrs
569+
.into_iter()
570+
.filter(|attr| attr.check_name(sym::rustc_mir))
571+
.flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
572+
573+
let borrowck_graphviz_postflow = rustc_mir_attrs
574+
.find(|attr| attr.check_name(sym::borrowck_graphviz_postflow))?;
575+
576+
let path_and_suffix = match borrowck_graphviz_postflow.value_str() {
577+
Some(p) => p,
578+
None => {
579+
tcx.sess.span_err(
580+
borrowck_graphviz_postflow.span(),
581+
"borrowck_graphviz_postflow requires a path",
582+
);
583+
584+
return None;
585+
}
586+
};
587+
588+
// Change "path/suffix.dot" to "path/analysis_name_suffix.dot"
589+
let mut ret = PathBuf::from(path_and_suffix.to_string());
590+
let suffix = ret.file_name().unwrap();
591+
592+
let mut file_name: OsString = analysis.into();
593+
file_name.push("_");
594+
file_name.push(suffix);
595+
ret.set_file_name(file_name);
596+
597+
Some(ret)
598+
}
599+
600+
fn write_dataflow_graphviz_results<A: Analysis<'tcx>>(
601+
body: &mir::Body<'tcx>,
602+
def_id: DefId,
603+
path: &Path,
604+
results: &Results<'tcx, A>
605+
) -> io::Result<()> {
606+
debug!("printing dataflow results for {:?} to {}", def_id, path.display());
607+
608+
let mut buf = Vec::new();
609+
let graphviz = graphviz::Formatter::new(body, def_id, results);
610+
611+
dot::render(&graphviz, &mut buf)?;
612+
fs::write(path, buf)
613+
}

0 commit comments

Comments
 (0)