mmtk/util/sanity/
sanity_checker.rs

1use crate::plan::Plan;
2use crate::scheduler::gc_work::*;
3use crate::util::ObjectReference;
4use crate::vm::slot::Slot;
5use crate::vm::*;
6use crate::MMTK;
7use crate::{scheduler::*, ObjectQueue};
8use std::collections::HashSet;
9use std::ops::{Deref, DerefMut};
10
11#[allow(dead_code)]
12pub struct SanityChecker<SL: Slot> {
13    /// Visited objects
14    refs: HashSet<ObjectReference>,
15    /// Cached root slots for sanity root scanning
16    root_slots: Vec<Vec<SL>>,
17    /// Cached root nodes for sanity root scanning
18    root_nodes: Vec<Vec<ObjectReference>>,
19}
20
21impl<SL: Slot> Default for SanityChecker<SL> {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27impl<SL: Slot> SanityChecker<SL> {
28    pub fn new() -> Self {
29        Self {
30            refs: HashSet::new(),
31            root_slots: vec![],
32            root_nodes: vec![],
33        }
34    }
35
36    /// Cache a list of root slots to the sanity checker.
37    pub fn add_root_slots(&mut self, roots: Vec<SL>) {
38        self.root_slots.push(roots)
39    }
40
41    pub fn add_root_nodes(&mut self, roots: Vec<ObjectReference>) {
42        self.root_nodes.push(roots)
43    }
44
45    /// Reset roots cache at the end of the sanity gc.
46    fn clear_roots_cache(&mut self) {
47        self.root_slots.clear();
48        self.root_nodes.clear();
49    }
50}
51
52pub struct ScheduleSanityGC<P: Plan> {
53    _plan: &'static P,
54}
55
56impl<P: Plan> ScheduleSanityGC<P> {
57    pub fn new(plan: &'static P) -> Self {
58        ScheduleSanityGC { _plan: plan }
59    }
60}
61
62impl<P: Plan> GCWork<P::VM> for ScheduleSanityGC<P> {
63    fn do_work(&mut self, worker: &mut GCWorker<P::VM>, mmtk: &'static MMTK<P::VM>) {
64        let scheduler = worker.scheduler();
65        let plan = mmtk.get_plan();
66
67        scheduler.reset_state();
68
69        // We are going to do sanity GC which will traverse the object graph again. Reset slot logger to clear recorded slots.
70        #[cfg(feature = "extreme_assertions")]
71        mmtk.slot_logger.reset();
72
73        mmtk.sanity_begin(); // Stop & scan mutators (mutator scanning can happen before STW)
74
75        // We use the cached roots for sanity gc, based on the assumption that
76        // the stack scanning triggered by the selected plan is correct and precise.
77        // FIXME(Wenyu,Tianle): When working on eager stack scanning on OpenJDK,
78        // the stack scanning may be broken. Uncomment the following lines to
79        // collect the roots again.
80        // Also, remember to call `DerivedPointerTable::update_pointers(); DerivedPointerTable::clear();`
81        // in openjdk binding before the second round of roots scanning.
82        // for mutator in <P::VM as VMBinding>::VMActivePlan::mutators() {
83        //     scheduler.work_buckets[WorkBucketStage::Prepare]
84        //         .add(ScanMutatorRoots::<SanityGCProcessEdges<P::VM>>(mutator));
85        // }
86        {
87            let sanity_checker = mmtk.sanity_checker.lock().unwrap();
88            for roots in &sanity_checker.root_slots {
89                scheduler.work_buckets[WorkBucketStage::Closure].add(
90                    SanityGCProcessEdges::<P::VM>::new(
91                        roots.clone(),
92                        true,
93                        mmtk,
94                        WorkBucketStage::Closure,
95                    ),
96                );
97            }
98            for roots in &sanity_checker.root_nodes {
99                scheduler.work_buckets[WorkBucketStage::Closure].add(ProcessRootNodes::<
100                    P::VM,
101                    SanityGCProcessEdges<P::VM>,
102                    SanityGCProcessEdges<P::VM>,
103                >::new(
104                    roots.clone(),
105                    WorkBucketStage::Closure,
106                ));
107            }
108        }
109        // Prepare global/collectors/mutators
110        worker.scheduler().work_buckets[WorkBucketStage::Prepare]
111            .add(SanityPrepare::<P>::new(plan.downcast_ref::<P>().unwrap()));
112        // Release global/collectors/mutators
113        worker.scheduler().work_buckets[WorkBucketStage::Release]
114            .add(SanityRelease::<P>::new(plan.downcast_ref::<P>().unwrap()));
115    }
116}
117
118pub struct SanityPrepare<P: Plan> {
119    pub plan: &'static P,
120}
121
122impl<P: Plan> SanityPrepare<P> {
123    pub fn new(plan: &'static P) -> Self {
124        Self { plan }
125    }
126}
127
128impl<P: Plan> GCWork<P::VM> for SanityPrepare<P> {
129    fn do_work(&mut self, _worker: &mut GCWorker<P::VM>, mmtk: &'static MMTK<P::VM>) {
130        info!("Sanity GC prepare");
131        {
132            let mut sanity_checker = mmtk.sanity_checker.lock().unwrap();
133            sanity_checker.refs.clear();
134        }
135    }
136}
137
138pub struct SanityRelease<P: Plan> {
139    pub plan: &'static P,
140}
141
142impl<P: Plan> SanityRelease<P> {
143    pub fn new(plan: &'static P) -> Self {
144        Self { plan }
145    }
146}
147
148impl<P: Plan> GCWork<P::VM> for SanityRelease<P> {
149    fn do_work(&mut self, _worker: &mut GCWorker<P::VM>, mmtk: &'static MMTK<P::VM>) {
150        info!("Sanity GC release");
151        mmtk.sanity_checker.lock().unwrap().clear_roots_cache();
152        mmtk.sanity_end();
153    }
154}
155
156// #[derive(Default)]
157pub struct SanityGCProcessEdges<VM: VMBinding> {
158    base: ProcessEdgesBase<VM>,
159}
160
161impl<VM: VMBinding> Deref for SanityGCProcessEdges<VM> {
162    type Target = ProcessEdgesBase<VM>;
163    fn deref(&self) -> &Self::Target {
164        &self.base
165    }
166}
167
168impl<VM: VMBinding> DerefMut for SanityGCProcessEdges<VM> {
169    fn deref_mut(&mut self) -> &mut Self::Target {
170        &mut self.base
171    }
172}
173
174impl<VM: VMBinding> ProcessEdgesWork for SanityGCProcessEdges<VM> {
175    type VM = VM;
176    type ScanObjectsWorkType = ScanObjects<Self>;
177
178    const OVERWRITE_REFERENCE: bool = false;
179    fn new(
180        slots: Vec<SlotOf<Self>>,
181        roots: bool,
182        mmtk: &'static MMTK<VM>,
183        bucket: WorkBucketStage,
184    ) -> Self {
185        Self {
186            base: ProcessEdgesBase::new(slots, roots, mmtk, bucket),
187            // ..Default::default()
188        }
189    }
190
191    fn trace_object(&mut self, object: ObjectReference) -> ObjectReference {
192        let mut sanity_checker = self.mmtk().sanity_checker.lock().unwrap();
193        if !sanity_checker.refs.contains(&object) {
194            // FIXME steveb consider VM-specific integrity check on reference.
195            assert!(object.is_sane(), "Invalid reference {:?}", object);
196
197            // Let plan check object
198            assert!(
199                self.mmtk().get_plan().sanity_check_object(object),
200                "Invalid reference {:?}",
201                object
202            );
203
204            // Let VM check object
205            assert!(
206                VM::VMObjectModel::is_object_sane(object),
207                "Invalid reference {:?}",
208                object
209            );
210
211            // Object is not "marked"
212            sanity_checker.refs.insert(object); // "Mark" it
213            trace!("Sanity mark object {}", object);
214            self.nodes.enqueue(object);
215        }
216
217        // If the valid object (VO) bit metadata is enabled, all live objects should have the VO
218        // bit set when sanity GC starts.
219        #[cfg(feature = "vo_bit")]
220        if !crate::util::metadata::vo_bit::is_vo_bit_set(object) {
221            panic!("VO bit is not set: {}", object);
222        }
223
224        object
225    }
226
227    fn create_scan_work(&self, nodes: Vec<ObjectReference>) -> Self::ScanObjectsWorkType {
228        ScanObjects::<Self>::new(nodes, false, WorkBucketStage::Closure)
229    }
230}