|
| 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; |
| 30 | +use std::cell::{BorrowState, RefCell}; |
| 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 | + |
| 67 | + pub fn enqueue(&self, message: &DepMessage) { |
| 68 | + if ENABLED { |
| 69 | + match self.stack.borrow_state() { |
| 70 | + BorrowState::Unused => {} |
| 71 | + _ => { |
| 72 | + // When we apply edge filters, that invokes the |
| 73 | + // Debug trait on DefIds, which in turn reads from |
| 74 | + // various bits of state and creates reads! Ignore |
| 75 | + // those recursive reads. |
| 76 | + return; |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + let mut stack = self.stack.borrow_mut(); |
| 81 | + match *message { |
| 82 | + DepMessage::Read(ref n) => self.check_edge(Some(Some(n)), top(&stack)), |
| 83 | + DepMessage::Write(ref n) => self.check_edge(top(&stack), Some(Some(n))), |
| 84 | + DepMessage::PushTask(ref n) => stack.push(Some(n.clone())), |
| 85 | + DepMessage::PushIgnore => stack.push(None), |
| 86 | + DepMessage::PopTask(ref n) => { |
| 87 | + match stack.pop() { |
| 88 | + Some(Some(m)) => { |
| 89 | + if *n != m { |
| 90 | + bug!("stack mismatch: found {:?} expected {:?}", m, n) |
| 91 | + } |
| 92 | + } |
| 93 | + Some(None) => bug!("stack mismatch: found Ignore expected {:?}", n), |
| 94 | + None => bug!("stack mismatch: found empty stack, expected {:?}", n), |
| 95 | + } |
| 96 | + } |
| 97 | + DepMessage::PopIgnore => { |
| 98 | + match stack.pop() { |
| 99 | + Some(Some(m)) => bug!("stack mismatch: found {:?} expected ignore", m), |
| 100 | + Some(None) => (), |
| 101 | + None => bug!("stack mismatch: found empty stack, expected ignore"), |
| 102 | + } |
| 103 | + } |
| 104 | + DepMessage::Query => (), |
| 105 | + } |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + fn check_edge(&self, |
| 110 | + source: Option<Option<&DepNode<DefId>>>, |
| 111 | + target: Option<Option<&DepNode<DefId>>>) { |
| 112 | + assert!(ENABLED); |
| 113 | + match (source, target) { |
| 114 | + // cannot happen, one side is always Some(Some(_)) |
| 115 | + (None, None) => unreachable!(), |
| 116 | + |
| 117 | + // nothing on top of the stack |
| 118 | + (None, Some(n)) | (Some(n), None) => bug!("read/write of {:?} but no current task", n), |
| 119 | + |
| 120 | + // this corresponds to an Ignore being top of the stack |
| 121 | + (Some(None), _) | (_, Some(None)) => (), |
| 122 | + |
| 123 | + // a task is on top of the stack |
| 124 | + (Some(Some(source)), Some(Some(target))) => { |
| 125 | + if let Some(ref forbidden_edge) = self.forbidden_edge { |
| 126 | + if forbidden_edge.test(source, target) { |
| 127 | + bug!("forbidden edge {:?} -> {:?} created", source, target) |
| 128 | + } |
| 129 | + } |
| 130 | + } |
| 131 | + } |
| 132 | + } |
| 133 | +} |
| 134 | + |
| 135 | +// Do a little juggling: we get back a reference to an option at the |
| 136 | +// top of the stack, convert it to an optional reference. |
| 137 | +fn top<'s>(stack: &'s Vec<Option<DepNode<DefId>>>) -> Option<Option<&'s DepNode<DefId>>> { |
| 138 | + stack.last() |
| 139 | + .map(|n: &'s Option<DepNode<DefId>>| -> Option<&'s DepNode<DefId>> { |
| 140 | + // (*) |
| 141 | + // (*) type annotation just there to clarify what would |
| 142 | + // otherwise be some *really* obscure code |
| 143 | + n.as_ref() |
| 144 | + }) |
| 145 | +} |
0 commit comments