]>
Commit | Line | Data |
---|---|---|
9e0c209e SL |
1 | // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! The "Shadow Graph" is maintained on the main thread and which | |
12 | //! tracks each message relating to the dep-graph and applies some | |
13 | //! sanity checks as they go by. If an error results, it means you get | |
14 | //! a nice stack-trace telling you precisely what caused the error. | |
15 | //! | |
16 | //! NOTE: This is a debugging facility which can potentially have non-trivial | |
17 | //! runtime impact. Therefore, it is largely compiled out if | |
18 | //! debug-assertions are not enabled. | |
19 | //! | |
20 | //! The basic sanity check, enabled if you have debug assertions | |
21 | //! enabled, is that there is always a task (or ignore) on the stack | |
22 | //! when you do read/write, and that the tasks are pushed/popped | |
23 | //! according to a proper stack discipline. | |
24 | //! | |
25 | //! Optionally, if you specify RUST_FORBID_DEP_GRAPH_EDGE, you can | |
26 | //! specify an edge filter to be applied to each edge as it is | |
27 | //! created. See `./README.md` for details. | |
28 | ||
29 | use hir::def_id::DefId; | |
476ff2be | 30 | use std::cell::RefCell; |
9e0c209e SL |
31 | use std::env; |
32 | ||
33 | use super::DepNode; | |
34 | use super::thread::DepMessage; | |
35 | use super::debug::EdgeFilter; | |
36 | ||
37 | pub struct ShadowGraph { | |
38 | // if you push None onto the stack, that corresponds to an Ignore | |
39 | stack: RefCell<Vec<Option<DepNode<DefId>>>>, | |
40 | forbidden_edge: Option<EdgeFilter>, | |
41 | } | |
42 | ||
43 | const ENABLED: bool = cfg!(debug_assertions); | |
44 | ||
45 | impl ShadowGraph { | |
46 | pub fn new() -> Self { | |
47 | let forbidden_edge = if !ENABLED { | |
48 | None | |
49 | } else { | |
50 | match env::var("RUST_FORBID_DEP_GRAPH_EDGE") { | |
51 | Ok(s) => { | |
52 | match EdgeFilter::new(&s) { | |
53 | Ok(f) => Some(f), | |
54 | Err(err) => bug!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err), | |
55 | } | |
56 | } | |
57 | Err(_) => None, | |
58 | } | |
59 | }; | |
60 | ||
61 | ShadowGraph { | |
62 | stack: RefCell::new(vec![]), | |
63 | forbidden_edge: forbidden_edge, | |
64 | } | |
65 | } | |
66 | ||
c30ab7b3 SL |
67 | #[inline] |
68 | pub fn enabled(&self) -> bool { | |
69 | ENABLED | |
70 | } | |
71 | ||
9e0c209e SL |
72 | pub fn enqueue(&self, message: &DepMessage) { |
73 | if ENABLED { | |
476ff2be SL |
74 | if self.stack.try_borrow().is_err() { |
75 | // When we apply edge filters, that invokes the Debug trait on | |
76 | // DefIds, which in turn reads from various bits of state and | |
77 | // creates reads! Ignore those recursive reads. | |
78 | return; | |
9e0c209e SL |
79 | } |
80 | ||
81 | let mut stack = self.stack.borrow_mut(); | |
82 | match *message { | |
cc61c64b XL |
83 | // It is ok to READ shared state outside of a |
84 | // task. That can't do any harm (at least, the only | |
85 | // way it can do harm is by leaking that data into a | |
86 | // query or task, which would be a problem | |
87 | // anyway). What would be bad is WRITING to that | |
88 | // state. | |
89 | DepMessage::Read(_) => { } | |
9e0c209e SL |
90 | DepMessage::Write(ref n) => self.check_edge(top(&stack), Some(Some(n))), |
91 | DepMessage::PushTask(ref n) => stack.push(Some(n.clone())), | |
92 | DepMessage::PushIgnore => stack.push(None), | |
93 | DepMessage::PopTask(ref n) => { | |
94 | match stack.pop() { | |
95 | Some(Some(m)) => { | |
96 | if *n != m { | |
97 | bug!("stack mismatch: found {:?} expected {:?}", m, n) | |
98 | } | |
99 | } | |
100 | Some(None) => bug!("stack mismatch: found Ignore expected {:?}", n), | |
101 | None => bug!("stack mismatch: found empty stack, expected {:?}", n), | |
102 | } | |
103 | } | |
104 | DepMessage::PopIgnore => { | |
105 | match stack.pop() { | |
106 | Some(Some(m)) => bug!("stack mismatch: found {:?} expected ignore", m), | |
107 | Some(None) => (), | |
108 | None => bug!("stack mismatch: found empty stack, expected ignore"), | |
109 | } | |
110 | } | |
111 | DepMessage::Query => (), | |
112 | } | |
113 | } | |
114 | } | |
115 | ||
116 | fn check_edge(&self, | |
117 | source: Option<Option<&DepNode<DefId>>>, | |
118 | target: Option<Option<&DepNode<DefId>>>) { | |
119 | assert!(ENABLED); | |
120 | match (source, target) { | |
121 | // cannot happen, one side is always Some(Some(_)) | |
122 | (None, None) => unreachable!(), | |
123 | ||
124 | // nothing on top of the stack | |
cc61c64b | 125 | (None, Some(n)) | (Some(n), None) => bug!("write of {:?} but no current task", n), |
9e0c209e SL |
126 | |
127 | // this corresponds to an Ignore being top of the stack | |
128 | (Some(None), _) | (_, Some(None)) => (), | |
129 | ||
130 | // a task is on top of the stack | |
131 | (Some(Some(source)), Some(Some(target))) => { | |
132 | if let Some(ref forbidden_edge) = self.forbidden_edge { | |
133 | if forbidden_edge.test(source, target) { | |
134 | bug!("forbidden edge {:?} -> {:?} created", source, target) | |
135 | } | |
136 | } | |
137 | } | |
138 | } | |
139 | } | |
140 | } | |
141 | ||
142 | // Do a little juggling: we get back a reference to an option at the | |
143 | // top of the stack, convert it to an optional reference. | |
144 | fn top<'s>(stack: &'s Vec<Option<DepNode<DefId>>>) -> Option<Option<&'s DepNode<DefId>>> { | |
145 | stack.last() | |
146 | .map(|n: &'s Option<DepNode<DefId>>| -> Option<&'s DepNode<DefId>> { | |
147 | // (*) | |
148 | // (*) type annotation just there to clarify what would | |
149 | // otherwise be some *really* obscure code | |
150 | n.as_ref() | |
151 | }) | |
152 | } |