mmtk/util/sanity/
sanity_checker.rs

1use crate::plan::Plan;
2use crate::scheduler::GCWork;
3use crate::scheduler::GCWorker;
4use crate::scheduler::WorkBucketStage;
5use crate::util::scanning_helper;
6use crate::util::ObjectReference;
7use crate::vm::slot::Slot;
8use crate::vm::{ObjectModel, VMBinding};
9use crate::MMTK;
10use std::collections::HashSet;
11
12#[allow(dead_code)]
13pub struct SanityChecker<SL: Slot> {
14    /// Visited objects
15    refs: HashSet<ObjectReference>,
16    /// Cached root slots for sanity root scanning
17    root_slots: Vec<Vec<SL>>,
18    /// Cached root nodes for sanity root scanning
19    root_nodes: Vec<Vec<ObjectReference>>,
20}
21
22impl<SL: Slot> Default for SanityChecker<SL> {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl<SL: Slot> SanityChecker<SL> {
29    pub fn new() -> Self {
30        Self {
31            refs: HashSet::new(),
32            root_slots: vec![],
33            root_nodes: vec![],
34        }
35    }
36
37    /// Cache a list of root slots to the sanity checker.
38    pub fn add_root_slots(&mut self, roots: Vec<SL>) {
39        debug!("Added {} root slots", roots.len());
40        self.root_slots.push(roots)
41    }
42
43    pub fn add_root_nodes(&mut self, roots: Vec<ObjectReference>) {
44        debug!("Added {} root nodes", roots.len());
45        self.root_nodes.push(roots)
46    }
47
48    /// Reset roots cache at the end of the sanity gc.
49    fn clear_roots_cache(&mut self) {
50        debug!("Cleared roots cache");
51        self.root_slots.clear();
52        self.root_nodes.clear();
53    }
54}
55
56pub struct ScheduleSanityGC<P: Plan> {
57    plan: &'static P,
58}
59
60impl<P: Plan> ScheduleSanityGC<P> {
61    pub fn new(plan: &'static P) -> Self {
62        ScheduleSanityGC { plan }
63    }
64}
65
66impl<P: Plan> GCWork<P::VM> for ScheduleSanityGC<P> {
67    fn do_work(&mut self, worker: &mut GCWorker<P::VM>, mmtk: &'static MMTK<P::VM>) {
68        let scheduler = worker.scheduler();
69
70        scheduler.reset_state();
71
72        // We are going to do sanity GC which will traverse the object graph again. Reset slot logger to clear recorded slots.
73        #[cfg(feature = "extreme_assertions")]
74        mmtk.slot_logger.reset();
75
76        mmtk.sanity_begin(); // Stop & scan mutators (mutator scanning can happen before STW)
77
78        // Prepare global/collectors/mutators
79        worker.scheduler().work_buckets[WorkBucketStage::Prepare]
80            .add(SanityPrepare::<P>::new(self.plan));
81        // Do the transitive closure
82        worker.scheduler().work_buckets[WorkBucketStage::Closure]
83            .add(SanityClosure::<P>::new(self.plan));
84        // Release global/collectors/mutators
85        worker.scheduler().work_buckets[WorkBucketStage::Release]
86            .add(SanityRelease::<P>::new(self.plan));
87    }
88}
89
90pub struct SanityPrepare<P: Plan> {
91    pub plan: &'static P,
92}
93
94impl<P: Plan> SanityPrepare<P> {
95    pub fn new(plan: &'static P) -> Self {
96        Self { plan }
97    }
98}
99
100impl<P: Plan> GCWork<P::VM> for SanityPrepare<P> {
101    fn do_work(&mut self, _worker: &mut GCWorker<P::VM>, mmtk: &'static MMTK<P::VM>) {
102        info!("Sanity GC prepare");
103        {
104            let mut sanity_checker = mmtk.sanity_checker.lock().unwrap();
105            sanity_checker.refs.clear();
106        }
107    }
108}
109
110pub struct SanityRelease<P: Plan> {
111    pub plan: &'static P,
112}
113
114impl<P: Plan> SanityRelease<P> {
115    pub fn new(plan: &'static P) -> Self {
116        Self { plan }
117    }
118}
119
120impl<P: Plan> GCWork<P::VM> for SanityRelease<P> {
121    fn do_work(&mut self, _worker: &mut GCWorker<P::VM>, mmtk: &'static MMTK<P::VM>) {
122        info!("Sanity GC release");
123        mmtk.sanity_checker.lock().unwrap().clear_roots_cache();
124        mmtk.sanity_end();
125    }
126}
127
128pub struct SanityClosure<P: Plan> {
129    pub plan: &'static P,
130}
131
132impl<P: Plan> SanityClosure<P> {
133    pub fn new(plan: &'static P) -> Self {
134        Self { plan }
135    }
136}
137
138impl<P: Plan> GCWork<P::VM> for SanityClosure<P> {
139    fn do_work(&mut self, worker: &mut GCWorker<P::VM>, mmtk: &'static MMTK<P::VM>) {
140        info!("Sanity GC closure");
141        let mut sanity_checker = mmtk.sanity_checker.lock().unwrap();
142
143        let mut queue = Vec::new();
144
145        // We use the cached roots for sanity gc, based on the assumption that
146        // the stack scanning triggered by the selected plan is correct and precise.
147        // FIXME(Wenyu,Tianle): When working on eager stack scanning on OpenJDK,
148        // the stack scanning may be broken. Uncomment the following lines to
149        // collect the roots again.
150        // Also, remember to call `DerivedPointerTable::update_pointers(); DerivedPointerTable::clear();`
151        // in openjdk binding before the second round of roots scanning.
152        // for mutator in <P::VM as VMBinding>::VMActivePlan::mutators() {
153        //     scheduler.work_buckets[WorkBucketStage::Prepare]
154        //         .add(ScanMutatorRoots::<SanityGCProcessEdges<P::VM>>(mutator));
155        // }
156        for roots in &sanity_checker.root_slots {
157            queue.extend(roots.iter().flat_map(|slot| slot.load()));
158        }
159        for roots in &sanity_checker.root_nodes {
160            queue.extend(roots);
161        }
162
163        let tls = worker.tls;
164
165        while let Some(object) = queue.pop() {
166            if !sanity_checker.refs.insert(object) {
167                continue;
168            }
169
170            trace!("Doing sanity check on object {object}");
171
172            // FIXME steveb consider VM-specific integrity check on reference.
173            assert!(
174                object.is_sane(),
175                "`object.is_sane()` returned false.  object: {object}",
176            );
177
178            // Let plan check object
179            assert!(
180                self.plan.sanity_check_object(object),
181                "plan.sanity_check_object(object) returned false. object: {object}",
182            );
183
184            // Let VM check object
185            assert!(
186                <P::VM as VMBinding>::VMObjectModel::is_object_sane(object),
187                "VMObjectModel::is_object_sane(object) returned false. object: {object}",
188            );
189
190            // Enqueue children.  If a child is already visited, it will be skipped at the beginning
191            // of the loop.
192            scanning_helper::visit_children_non_moving::<P::VM>(tls, object, &mut |child| {
193                queue.push(child);
194                child
195            });
196        }
197    }
198}