Skip to content

Commit fc109db

Browse files
authored
Merge pull request #92 from mark-i-m/deptrack
Add incremental compilation debugging subchapter
2 parents 52787a6 + 1131ff4 commit fc109db

File tree

2 files changed

+114
-0
lines changed

2 files changed

+114
-0
lines changed

Diff for: src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- [Rustdoc](./rustdoc.md)
1414
- [Queries: demand-driven compilation](./query.md)
1515
- [Incremental compilation](./incremental-compilation.md)
16+
- [Debugging and Testing](./incrcomp-debugging.md)
1617
- [The parser](./the-parser.md)
1718
- [Macro expansion](./macro-expansion.md)
1819
- [Name resolution](./name-resolution.md)

Diff for: src/incrcomp-debugging.md

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Debugging and Testing Dependencies
2+
3+
## Testing the dependency graph
4+
5+
There are various ways to write tests against the dependency graph.
6+
The simplest mechanisms are the `#[rustc_if_this_changed]` and
7+
`#[rustc_then_this_would_need]` annotations. These are used in compile-fail
8+
tests to test whether the expected set of paths exist in the dependency graph.
9+
As an example, see `src/test/compile-fail/dep-graph-caller-callee.rs`.
10+
11+
The idea is that you can annotate a test like:
12+
13+
```rust
14+
#[rustc_if_this_changed]
15+
fn foo() { }
16+
17+
#[rustc_then_this_would_need(TypeckTables)] //~ ERROR OK
18+
fn bar() { foo(); }
19+
20+
#[rustc_then_this_would_need(TypeckTables)] //~ ERROR no path
21+
fn baz() { }
22+
```
23+
24+
This will check whether there is a path in the dependency graph from `Hir(foo)`
25+
to `TypeckTables(bar)`. An error is reported for each
26+
`#[rustc_then_this_would_need]` annotation that indicates whether a path
27+
exists. `//~ ERROR` annotations can then be used to test if a path is found (as
28+
demonstrated above).
29+
30+
## Debugging the dependency graph
31+
32+
### Dumping the graph
33+
34+
The compiler is also capable of dumping the dependency graph for your
35+
debugging pleasure. To do so, pass the `-Z dump-dep-graph` flag. The
36+
graph will be dumped to `dep_graph.{txt,dot}` in the current
37+
directory. You can override the filename with the `RUST_DEP_GRAPH`
38+
environment variable.
39+
40+
Frequently, though, the full dep graph is quite overwhelming and not
41+
particularly helpful. Therefore, the compiler also allows you to filter
42+
the graph. You can filter in three ways:
43+
44+
1. All edges originating in a particular set of nodes (usually a single node).
45+
2. All edges reaching a particular set of nodes.
46+
3. All edges that lie between given start and end nodes.
47+
48+
To filter, use the `RUST_DEP_GRAPH_FILTER` environment variable, which should
49+
look like one of the following:
50+
51+
```
52+
source_filter // nodes originating from source_filter
53+
-> target_filter // nodes that can reach target_filter
54+
source_filter -> target_filter // nodes in between source_filter and target_filter
55+
```
56+
57+
`source_filter` and `target_filter` are a `&`-separated list of strings.
58+
A node is considered to match a filter if all of those strings appear in its
59+
label. So, for example:
60+
61+
```
62+
RUST_DEP_GRAPH_FILTER='-> TypeckTables'
63+
```
64+
65+
would select the predecessors of all `TypeckTables` nodes. Usually though you
66+
want the `TypeckTables` node for some particular fn, so you might write:
67+
68+
```
69+
RUST_DEP_GRAPH_FILTER='-> TypeckTables & bar'
70+
```
71+
72+
This will select only the predecessors of `TypeckTables` nodes for functions with
73+
`bar` in their name.
74+
75+
Perhaps you are finding that when you change `foo` you need to re-type-check `bar`,
76+
but you don't think you should have to. In that case, you might do:
77+
78+
```
79+
RUST_DEP_GRAPH_FILTER='Hir & foo -> TypeckTables & bar'
80+
```
81+
82+
This will dump out all the nodes that lead from `Hir(foo)` to
83+
`TypeckTables(bar)`, from which you can (hopefully) see the source
84+
of the erroneous edge.
85+
86+
### Tracking down incorrect edges
87+
88+
Sometimes, after you dump the dependency graph, you will find some
89+
path that should not exist, but you will not be quite sure how it came
90+
to be. **When the compiler is built with debug assertions,** it can
91+
help you track that down. Simply set the `RUST_FORBID_DEP_GRAPH_EDGE`
92+
environment variable to a filter. Every edge created in the dep-graph
93+
will be tested against that filter -- if it matches, a `bug!` is
94+
reported, so you can easily see the backtrace (`RUST_BACKTRACE=1`).
95+
96+
The syntax for these filters is the same as described in the previous
97+
section. However, note that this filter is applied to every **edge**
98+
and doesn't handle longer paths in the graph, unlike the previous
99+
section.
100+
101+
Example:
102+
103+
You find that there is a path from the `Hir` of `foo` to the type
104+
check of `bar` and you don't think there should be. You dump the
105+
dep-graph as described in the previous section and open `dep-graph.txt`
106+
to see something like:
107+
108+
Hir(foo) -> Collect(bar)
109+
Collect(bar) -> TypeckTables(bar)
110+
111+
That first edge looks suspicious to you. So you set
112+
`RUST_FORBID_DEP_GRAPH_EDGE` to `Hir&foo -> Collect&bar`, re-run, and
113+
then observe the backtrace. Voila, bug fixed!

0 commit comments

Comments
 (0)