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 flow-graph visualization (via graphviz) to rustc #14202

Closed
wants to merge 6 commits into from

Conversation

pnkfelix
Copy link
Member

Passing --pretty=flowgraph makes rustc print a control flow graph.

In practice, you will also need to pass the additional options:

  • -o <FILE> to emit output to a .dot file for graphviz, and
  • --pretty-node=<ID> to specify the node-id for the block you want to print.

(You can only print the flow-graph for a particular block in the AST.)

These changes do not generalize the rest of the pretty-printer to
support the --pretty-node=<ID> option for printing subtrees of the
AST, but that is obviously a potential future feature that we could
add on. It is orthogonal to flow-graph printing, though.

@pnkfelix
Copy link
Member Author

oh wait, cant merge yet, I'll rebase.

@pnkfelix pnkfelix closed this May 14, 2014
pnkfelix added 4 commits May 14, 2014 22:25
Add `EntryPat` and `NodePat` variants to ast_map, so that lookups for
id 1 in `let S{val: _x /* pat 2 */} /* pat 1 */ = ...` will actually
resolve to the pattern `S{ ... }`, rather than "unknown node", in a
function like `node_id_to_str`.
Refine lifetimes in signature for graph node/edge iteration methods.

Added `pub` `node_id` and `edge_id` methods that correspond to
NodeIndex and EdgeIndex `get` methods (note that the inner index is
already `pub` in the struct definitions).  (I decided that `get()`,
used internally, just looks too generic and that client code is
clearer with more explicit method names.)
1. Only insert non-dummy nodes into the exit map.

2. Revise handling of `break` and `continue` forms so that they are
   not treated as if control falls through to the next node (since it
   does not, it just jumps to the end or start of the loop body).

3. Fixed support for return expression in flow graph construction.
@pnkfelix pnkfelix reopened this May 14, 2014
@pnkfelix
Copy link
Member Author

r? anyone

@pnkfelix
Copy link
Member Author

@nikomatsakis : while i did say "r? anyone", you may be particularly interested in looking at this.

@@ -515,6 +515,8 @@ pub fn optgroups() -> Vec<getopts::OptGroup> {
optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
optopt( "", "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"),
optflag("", "parse-only", "Parse only; do not compile, assemble, or link"),
optflagopt("", "pretty-node",
"Pretty-print subtree of input, identified by node-id argument", "NODE"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a fairly specific argument to rustc which is taking a very prominent position as a top-level argument. Perhaps this could be transmitted through some other means?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be a -Z argument?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--pretty flowgraph=<somenodeid>?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into changing to --pretty flowgraph=<somenodeid>, since I think that will satisfy all parties involved.

(My thinking was that the --pretty-node flag would eventually be generalized to apply to the rest of the pretty-printer, but adding a top-level flag for that hypothetical generalization was pretty premature.)

@alexcrichton
Copy link
Member

This looks great to me, nice set of tests! I'd like to prevent adding another top-level command line argument, but other than that I'd be fine r+'ing this

// \l, not the line that follows; so, add \l at end of string
// if not already present, ensuring last line gets left-aligned
// as well.
let mut last_two : Vec<_> = s.chars().rev().take(2).collect();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can just be s.ends_with("\\l")?

pnkfelix added 2 commits May 15, 2014 19:46
Passing `--pretty flowgraph=<NODEID>` makes rustc print a control flow graph.

In pratice, you will also need to pass the additional option:
`-o <FILE>` to emit output to a `.dot` file for graphviz.

(You can only print the flow-graph for a particular block in the AST.)

----

An interesting implementation detail is the way the code puts both the
node index (`cfg::CFGIndex`) and a reference to the payload
(`cfg::CFGNode`) into the single `Node` type that is used for
labelling and walking the graph.  I had once mistakenly thought that I
only wanted the `cfg::CFGNode`, but for labelling, you really want the
cfg index too, rather than e.g. trying to use the `ast::NodeId` as the
label (which breaks down e.g. due to `ast::DUMMY_NODE_ID`).

----

As a drive-by fix, I had to fix `rustc::middle::cfg::construct`
interface to reflect changes that have happened on the master branch
while I was getting this integrated into the compiler.  (The next
commit actually adds tests of the `--pretty flowgraph` functionality,
so that should ensure that the `rustc::middle::cfg` code does not go
stale again.)
Each test works by rendering the flowgraph for the last identified
block we see in expanded pretty-printed output, and comparing it (via
`diff`) against a checked in "foo.dot-expected.dot" file.

Each test post-processes the output to remove NodeIds ` (id=NUM)` so
that the expected output is somewhat stable (or at least independent
of how we assign NodeIds) and easier for a human to interpret when
looking at the expected output file itself.

----

Test writing style notes:

I usually tried to write the tests in a way that would avoid duplicate
labels in the output rendered flow graph, when possible.

The tests that have string literals "unreachable" in the program text
are deliberately written that way to remind the reader that the
unreachable nodes in the resulting graph are not an error in the
control flow computation, but rather a natural consequence of its
construction.
@emberian
Copy link
Member

Fantastic!

@ghost
Copy link

ghost commented Nov 4, 2018

Is there any way to see the whole(or even part, like OP says) AST via graphviz output with current rustc ?
--pretty=flowgraph doesn't seem available anymore

$ rustc --pretty=flowgraph -o graphviz.dot -Z unstable-options ./main.rs 
error: argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified`; got flowgraph
rustc 1.32.0-nightly (04fdb44f5 2018-11-03)
binary: rustc
commit-hash: 04fdb44f5ccb6f909cf0f768781afa483e97097c
commit-date: 2018-11-03
host: x86_64-unknown-linux-gnu
release: 1.32.0-nightly
LLVM version: 8.0

EDIT: What I tried:
from here
added these to my main.rs at the top

#![feature(rustc_attrs)]
#![rustc_mir(borrowck_graphviz_postflow="suffix.dot")]     

But $ find ./workspacerootdir/ -iname \*suffix.dot\* and locate --regex '.*suffix.dot' yielded no results.

EDIT2: found another doc:

For a graphviz-rendering with dataflow annotations, add the attribute

EDIT3: Alright, it works if I add it to the function, like:

#[rustc_mir(borrowck_graphviz_postflow="/tmp/suffix.dot")]                      
fn main() {

@eddyb
Copy link
Member

eddyb commented Nov 4, 2018

@xftroxgpx It's probably under --unpretty or -Z.
Note that this is the Control-Flow Graph, not the AST itself.
This CFG will go away once only the NLL borrowck remains.
You probably want to look at MIR instead - IIRC there's a way to dump that as a graphviz graph, but I don't know offhand - cc @oli-obk @solson @nikomatsakis.

@ghost
Copy link

ghost commented Nov 4, 2018

Thanks @eddyb ! I found the MIR stuff you were referring to here:

For a graphviz-rendering with dataflow annotations, add the attribute

eg.

#![feature(rustc_attrs)]                                                        
...
#[rustc_mir(borrowck_graphviz_postflow="/tmp/suffix.dot")]                      
fn main() {

And it works! But as you said, it's not the AST. Would love to see AST graph, if that exists.

EDIT: also found the unpretty ones

$ rustc ./main.rs --pretty=identified -Z unstable-options

get number from end of main's } paren which is 4294967295 (EDIT2: actually they are all the same number!) this is issue #10671

$ rustc ./main.rs -Z unpretty=flowgraph=4294967295 -Z flowgraph-print-loans -Z flowgraph-print-moves -Z flowgraph-print-assigns -Z flowgraph-print-all 

this ICEs:

thread 'main' panicked at 'assertion failed: x < (u32::MAX as usize)', libsyntax/ast.rs:221:9
note: Run with `RUST_BACKTRACE=1` for a backtrace.

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.32.0-nightly (04fdb44f5 2018-11-03) running on x86_64-unknown-linux-gnu

note: compiler flags: -Z unpretty=flowgraph=4294967295 -Z flowgraph-print-loans -Z flowgraph-print-moves -Z flowgraph-print-assigns -Z flowgraph-print-all -Z unstable-options

EDIT4: ok this works:

$ rustc ./main.rs --pretty=expanded,identified -Z unstable-options

shows } /* block 117 */ /* 114 */ at end of main, use 114 not 117 below:

$ rustc ./main.rs -Z unpretty=flowgraph=114 -Z flowgraph-print-loans -Z flowgraph-print-moves -Z flowgraph-print-assigns -Z flowgraph-print-all -o gv.dot
$ xdot gv.dot

EDIT5: Currently -Z flowgraph-print-all is the same as -Z flowgraph-print-loans -Z flowgraph-print-moves -Z flowgraph-print-assigns, so no need to use them above.

@eddyb
Copy link
Member

eddyb commented Nov 4, 2018

@xftroxgpx Do you want an AST CFG, or just an AST? Also, may I ask what for?
We're planning to gradually remove most of the semantic handling on ASTs, and ASTs (specifically, HIR, which is a slightly desugared and name-resolved AST) will primarily be used for type inference and lowering to MIR (once type-inferred).

@ghost
Copy link

ghost commented Nov 4, 2018

@eddyb Ah, just for learning purposes only. I was hoping that I could see, in a graph-way, how rust sees my vars, loans and assignments, but looks like it's just a Control-Flow Graph instead :( (just as you said)

that MIR one is what I want, lookslike. (if not a higher level one)

@eddyb
Copy link
Member

eddyb commented Nov 4, 2018

@xftroxgpx Ah, for that, you can just look at the textual MIR format.
We have no graph generation, AFAIK, that links together all the places a name (of a variable, for example) shows up - that would amount to a dataflow graph, I believe.

Maybe we should do a mixed control-flow and data-flow graph? Or even run a VSDG building algorithm on the MIR to get a data-flow graph that describes side-effects as linear "data".

lnicola pushed a commit to lnicola/rust that referenced this pull request Mar 13, 2023
minor: import `Either` from `either`

This is a clean-up patch to replace `use itertools::Either` with `use either::Either` for the sake of consistency.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants