diff --git a/macros/src/has_spaces_impl.rs b/macros/src/has_spaces_impl.rs index 71286e415d..4016e95c89 100644 --- a/macros/src/has_spaces_impl.rs +++ b/macros/src/has_spaces_impl.rs @@ -70,12 +70,12 @@ pub(crate) fn generate_impl_items<'a>( }; quote! { - fn for_each_space(&self, __func: &mut dyn FnMut(&dyn Space)) { + fn for_each_space<'a>(&'a self, __func: &mut dyn FnMut(&'a dyn Space)) { #(#space_visitors)* #parent_visitor } - fn for_each_space_mut(&mut self, __func: &mut dyn FnMut(&mut dyn Space)) { + fn for_each_space_mut<'a>(&'a mut self, __func: &mut dyn FnMut(&'a mut dyn Space)) { #(#space_visitors_mut)* #parent_visitor_mut } diff --git a/src/mmtk.rs b/src/mmtk.rs index e0a355108f..cc1c511cff 100644 --- a/src/mmtk.rs +++ b/src/mmtk.rs @@ -12,6 +12,7 @@ use crate::util::address::ObjectReference; use crate::util::analysis::AnalysisManager; use crate::util::finalizable_processor::FinalizableProcessor; use crate::util::heap::gc_trigger::GCTrigger; +use crate::util::heap::inspection::SpaceInspector; use crate::util::heap::layout::heap_parameters::MAX_SPACES; use crate::util::heap::layout::vm_layout::VMLayout; use crate::util::heap::layout::{self, Mmapper, VMMap}; @@ -596,4 +597,14 @@ impl MMTK { .vm_space .initialize_object_metadata(object, false) } + + /// Inspect MMTk spaces. The space inspector allows users to inspect the heap hierarchically, + /// with all levels of regions. Users can further inspect objects in the regions if vo_bit is enabled. + pub fn inspect_spaces(&self) -> Vec<&dyn SpaceInspector> { + let mut ret = vec![]; + self.get_plan().for_each_space(&mut |space| { + ret.push(space.as_inspector()); + }); + ret + } } diff --git a/src/plan/global.rs b/src/plan/global.rs index 5a5bb38ab5..5f38c39908 100644 --- a/src/plan/global.rs +++ b/src/plan/global.rs @@ -714,13 +714,13 @@ pub trait HasSpaces { /// /// If `Self` contains nested fields that contain more spaces, this method shall visit spaces /// in the outer struct first. - fn for_each_space(&self, func: &mut dyn FnMut(&dyn Space)); + fn for_each_space<'a>(&'a self, func: &mut dyn FnMut(&'a dyn Space)); /// Visit each space field mutably. /// /// If `Self` contains nested fields that contain more spaces, this method shall visit spaces /// in the outer struct first. - fn for_each_space_mut(&mut self, func: &mut dyn FnMut(&mut dyn Space)); + fn for_each_space_mut<'a>(&'a mut self, func: &mut dyn FnMut(&'a mut dyn Space)); } /// A plan that uses `PlanProcessEdges` needs to provide an implementation for this trait. diff --git a/src/policy/copyspace.rs b/src/policy/copyspace.rs index bbc66ccd4d..318333100e 100644 --- a/src/policy/copyspace.rs +++ b/src/policy/copyspace.rs @@ -111,6 +111,10 @@ impl Space for CopySpace { self } + fn as_inspector(&self) -> &dyn crate::util::heap::inspection::SpaceInspector { + self + } + fn get_page_resource(&self) -> &dyn PageResource { &self.pr } @@ -160,6 +164,22 @@ impl crate::policy::gc_work::PolicyTraceObject for CopySpace< } } +use crate::util::heap::inspection::{RegionInspector, SpaceInspector}; +impl SpaceInspector for CopySpace { + fn list_top_regions(&self) -> Vec> { + crate::util::heap::inspection::into_regions::( + &mut self.pr.iterate_allocated_regions(), + ) + } + + fn list_sub_regions( + &self, + _parent_region: &dyn RegionInspector, + ) -> Vec> { + vec![] + } +} + impl CopySpace { pub fn new(args: crate::policy::space::PlanCreateSpaceArgs, from_space: bool) -> Self { let vm_map = args.vm_map; diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 7b0429406b..067868ba90 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -167,6 +167,9 @@ impl Space for ImmixSpace { fn as_sft(&self) -> &(dyn SFT + Sync + 'static) { self } + fn as_inspector(&self) -> &dyn crate::util::heap::inspection::SpaceInspector { + self + } fn get_page_resource(&self) -> &dyn PageResource { &self.pr } @@ -1178,3 +1181,31 @@ impl ClearVOBitsAfterPrepare { } } } + +mod inspector { + use super::*; + use crate::util::heap::inspection::{list_sub_regions, RegionInspector, SpaceInspector}; + impl SpaceInspector for ImmixSpace { + fn list_top_regions(&self) -> Vec> { + self.chunk_map + .all_chunks() + .map(|r: Chunk| Box::new(r) as Box) + .collect() + } + + fn list_sub_regions( + &self, + parent_region: &dyn RegionInspector, + ) -> Vec> { + if let Some(regions) = list_sub_regions::(parent_region) { + return regions; + } + if !crate::policy::immix::BLOCK_ONLY { + if let Some(regions) = list_sub_regions::(parent_region) { + return regions; + } + } + vec![] + } + } +} diff --git a/src/policy/immortalspace.rs b/src/policy/immortalspace.rs index c08ffeeaaa..208fe65930 100644 --- a/src/policy/immortalspace.rs +++ b/src/policy/immortalspace.rs @@ -96,6 +96,9 @@ impl Space for ImmortalSpace { fn as_sft(&self) -> &(dyn SFT + Sync + 'static) { self } + fn as_inspector(&self) -> &dyn crate::util::heap::inspection::SpaceInspector { + self + } fn get_page_resource(&self) -> &dyn PageResource { &self.pr } @@ -227,3 +230,23 @@ impl ImmortalSpace { object } } + +mod inspector { + use super::*; + use crate::util::heap::inspection::{RegionInspector, SpaceInspector}; + + impl SpaceInspector for ImmortalSpace { + fn list_top_regions(&self) -> Vec> { + crate::util::heap::inspection::into_regions::( + &mut self.pr.iterate_allocated_regions(), + ) + } + + fn list_sub_regions( + &self, + _parent_region: &dyn RegionInspector, + ) -> Vec> { + vec![] + } + } +} diff --git a/src/policy/largeobjectspace.rs b/src/policy/largeobjectspace.rs index d9f274d479..8df4716f4c 100644 --- a/src/policy/largeobjectspace.rs +++ b/src/policy/largeobjectspace.rs @@ -154,6 +154,9 @@ impl Space for LargeObjectSpace { fn as_sft(&self) -> &(dyn SFT + Sync + 'static) { self } + fn as_inspector(&self) -> &dyn crate::util::heap::inspection::SpaceInspector { + self + } fn get_page_resource(&self) -> &dyn PageResource { &self.pr } @@ -380,3 +383,24 @@ impl LargeObjectSpace { fn get_super_page(cell: Address) -> Address { cell.align_down(BYTES_IN_PAGE) } + +mod inspector { + use super::*; + use crate::util::heap::inspection::{RegionInspector, SpaceInspector}; + + impl SpaceInspector for LargeObjectSpace { + fn list_top_regions(&self) -> Vec> { + let space = unsafe { &*(self as *const Self) }; + vec![Box::new(crate::util::heap::inspection::SpaceAsRegion::new( + space, + ))] + } + + fn list_sub_regions( + &self, + _parent_region: &dyn RegionInspector, + ) -> Vec> { + vec![] + } + } +} diff --git a/src/policy/lockfreeimmortalspace.rs b/src/policy/lockfreeimmortalspace.rs index 76e44c55ee..a36b75ef09 100644 --- a/src/policy/lockfreeimmortalspace.rs +++ b/src/policy/lockfreeimmortalspace.rs @@ -108,6 +108,9 @@ impl Space for LockFreeImmortalSpace { fn as_sft(&self) -> &(dyn SFT + Sync + 'static) { self } + fn as_inspector(&self) -> &dyn crate::util::heap::inspection::SpaceInspector { + self + } fn get_page_resource(&self) -> &dyn PageResource { unimplemented!() } @@ -271,3 +274,24 @@ impl LockFreeImmortalSpace { space } } + +mod inspector { + use super::*; + use crate::util::heap::inspection::{RegionInspector, SpaceInspector}; + + impl SpaceInspector for LockFreeImmortalSpace { + fn list_top_regions(&self) -> Vec> { + let space = unsafe { &*(self as *const Self) }; + vec![Box::new(crate::util::heap::inspection::SpaceAsRegion::new( + space, + ))] + } + + fn list_sub_regions( + &self, + _parent_region: &dyn RegionInspector, + ) -> Vec> { + vec![] + } + } +} diff --git a/src/policy/markcompactspace.rs b/src/policy/markcompactspace.rs index d7ca0c0745..6092f7cd6a 100644 --- a/src/policy/markcompactspace.rs +++ b/src/policy/markcompactspace.rs @@ -113,6 +113,10 @@ impl Space for MarkCompactSpace { self } + fn as_inspector(&self) -> &dyn crate::util::heap::inspection::SpaceInspector { + self + } + fn get_page_resource(&self) -> &dyn PageResource { &self.pr } @@ -438,3 +442,23 @@ impl crate::util::linear_scan::LinearScanObjectSize for MarkCompa VM::VMObjectModel::get_current_size(object) } } + +mod inspector { + use super::*; + use crate::util::heap::inspection::{RegionInspector, SpaceInspector}; + + impl SpaceInspector for MarkCompactSpace { + fn list_top_regions(&self) -> Vec> { + crate::util::heap::inspection::into_regions::( + &mut self.pr.iterate_allocated_regions(), + ) + } + + fn list_sub_regions( + &self, + _parent_region: &dyn RegionInspector, + ) -> Vec> { + vec![] + } + } +} diff --git a/src/policy/marksweepspace/malloc_ms/global.rs b/src/policy/marksweepspace/malloc_ms/global.rs index fd473dd0ee..d617a85158 100644 --- a/src/policy/marksweepspace/malloc_ms/global.rs +++ b/src/policy/marksweepspace/malloc_ms/global.rs @@ -149,6 +149,10 @@ impl Space for MallocSpace { self } + fn as_inspector(&self) -> &dyn crate::util::heap::inspection::SpaceInspector { + self + } + fn get_page_resource(&self) -> &dyn PageResource { unreachable!() } @@ -895,3 +899,23 @@ impl GCWork for MSSweepChunk { self.ms.sweep_chunk(self.chunk); } } + +mod inspector { + use super::*; + use crate::util::heap::inspection::{RegionInspector, SpaceInspector}; + impl SpaceInspector for MallocSpace { + fn list_top_regions(&self) -> Vec> { + self.chunk_map + .all_chunks() + .map(|r: Chunk| Box::new(r) as Box) + .collect() + } + + fn list_sub_regions( + &self, + _parent_region: &dyn RegionInspector, + ) -> Vec> { + vec![] + } + } +} diff --git a/src/policy/marksweepspace/native_ms/global.rs b/src/policy/marksweepspace/native_ms/global.rs index 1dc87cd04c..1cf5600bd3 100644 --- a/src/policy/marksweepspace/native_ms/global.rs +++ b/src/policy/marksweepspace/native_ms/global.rs @@ -231,6 +231,10 @@ impl Space for MarkSweepSpace { self } + fn as_inspector(&self) -> &dyn crate::util::heap::inspection::SpaceInspector { + self + } + fn get_page_resource(&self) -> &dyn crate::util::heap::PageResource { &self.pr } @@ -667,3 +671,26 @@ impl Drop for RecycleBlocks { epilogue::debug_assert_counter_zero(&self.counter, "RecycleBlocks::counter"); } } + +mod inspector { + use super::*; + use crate::util::heap::inspection::{list_sub_regions, RegionInspector, SpaceInspector}; + impl SpaceInspector for MarkSweepSpace { + fn list_top_regions(&self) -> Vec> { + self.chunk_map + .all_chunks() + .map(|r: Chunk| Box::new(r) as Box) + .collect() + } + + fn list_sub_regions( + &self, + parent_region: &dyn RegionInspector, + ) -> Vec> { + if let Some(regions) = list_sub_regions::(parent_region) { + return regions; + } + vec![] + } + } +} diff --git a/src/policy/space.rs b/src/policy/space.rs index d300efbbf9..99e88ef5d7 100644 --- a/src/policy/space.rs +++ b/src/policy/space.rs @@ -2,6 +2,7 @@ use crate::global_state::GlobalState; use crate::plan::PlanConstraints; use crate::scheduler::GCWorkScheduler; use crate::util::conversions::*; +use crate::util::heap::inspection::SpaceInspector; use crate::util::metadata::side_metadata::{ SideMetadataContext, SideMetadataSanity, SideMetadataSpec, }; @@ -42,6 +43,7 @@ use downcast_rs::Downcast; pub trait Space: 'static + SFT + Sync + Downcast { fn as_space(&self) -> &dyn Space; fn as_sft(&self) -> &(dyn SFT + Sync + 'static); + fn as_inspector(&self) -> &dyn SpaceInspector; fn get_page_resource(&self) -> &dyn PageResource; /// Get a mutable reference to the underlying page resource, or `None` if the space does not diff --git a/src/policy/vmspace.rs b/src/policy/vmspace.rs index ab1101aaf5..6de4849db5 100644 --- a/src/policy/vmspace.rs +++ b/src/policy/vmspace.rs @@ -99,6 +99,9 @@ impl Space for VMSpace { fn as_sft(&self) -> &(dyn SFT + Sync + 'static) { self } + fn as_inspector(&self) -> &dyn crate::util::heap::inspection::SpaceInspector { + self + } fn get_page_resource(&self) -> &dyn PageResource { &self.pr } @@ -300,3 +303,24 @@ impl VMSpace { object } } + +mod inspector { + use super::*; + use crate::util::heap::inspection::{RegionInspector, SpaceInspector}; + + impl SpaceInspector for VMSpace { + fn list_top_regions(&self) -> Vec> { + let space = unsafe { &*(self as *const Self) }; + vec![Box::new(crate::util::heap::inspection::SpaceAsRegion::new( + space, + ))] + } + + fn list_sub_regions( + &self, + _parent_region: &dyn RegionInspector, + ) -> Vec> { + vec![] + } + } +} diff --git a/src/util/heap/freelistpageresource.rs b/src/util/heap/freelistpageresource.rs index aa66fe232d..852a1361c2 100644 --- a/src/util/heap/freelistpageresource.rs +++ b/src/util/heap/freelistpageresource.rs @@ -93,7 +93,6 @@ impl PageResource for FreeListPageResource { }; new_chunk = true; } - if page_offset == freelist::FAILURE { return Result::Err(PRAllocFail); } else { diff --git a/src/util/heap/inspection.rs b/src/util/heap/inspection.rs new file mode 100644 index 0000000000..5e4476adeb --- /dev/null +++ b/src/util/heap/inspection.rs @@ -0,0 +1,146 @@ +use crate::policy::sft::SFT; +use crate::policy::space::Space; +use crate::util::linear_scan::RegionIterator; +#[cfg(feature = "vo_bit")] +use crate::util::ObjectReference; +use crate::util::{linear_scan::Region, Address}; + +/// SpaceInspector allows users to inspect the heap in a hierarchical structure. +pub trait SpaceInspector: SFT +where + Self: 'static, +{ + /// The name of this space, given by the plan. + fn space_name(&self) -> &str { + SFT::name(self) + } + /// The name of this policy. + fn policy_name(&self) -> &str { + std::any::type_name::() + } + /// List the top-level regions used by this space. This is usually [`crate::util::heap::chunk_map::Chunk`] for most spaces. + /// If there is no region used by the space at the moment, it returns an empty Vector. + fn list_top_regions(&self) -> Vec>; + /// List sub regions of the given parent region if the space organises its heap in a heirarchical way. + /// The parent region could be the results from [`SpaceInspector::list_top_regions`] or the results from [`SpaceInspector::list_sub_regions`]. + /// If there is no sub regions for the given region, it returns an empty Vector. + fn list_sub_regions( + &self, + parent_region: &dyn RegionInspector, + ) -> Vec>; +} + +/// For the given region inspector, if it matches the PARENT region, return the sub regions of the CHILD region type. +/// Otherwise, return None. +pub(crate) fn list_sub_regions( + region: &dyn RegionInspector, +) -> Option>> { + if region.region_type() == std::any::type_name::() { + let start_child_region = CHILD::from_aligned_address(region.start()); + let end_child_region = CHILD::from_aligned_address(region.start() + region.size()); + Some( + RegionIterator::::new(start_child_region, end_child_region) + .map(|r| Box::new(r) as Box) + .collect(), + ) + } else { + None + } +} + +/// Convert an iterator of pairs of (region start, region size) into a vector of region inspector. +pub(crate) fn into_regions( + regions: &mut dyn Iterator, +) -> Vec> { + regions + .flat_map(|(start, size)| { + let mut current = start; + let end = start + size; + std::iter::from_fn(move || { + if current >= end { + return None; + } + let region = R::from_aligned_address(current); + current += R::BYTES; + Some(Box::new(region) as Box) + }) + }) + .collect() +} + +/// RegionInspector allows users to inspect a region of the heap. +pub trait RegionInspector { + /// The type of this region. + fn region_type(&self) -> &str; + /// The start address of this region. + fn start(&self) -> Address; + /// The byte size of this region. + fn size(&self) -> usize; + #[cfg(feature = "vo_bit")] + /// List all objects in this region. This is only available when `vo_bit` feature is enabled. + fn list_objects(&self) -> Vec { + let mut objects = vec![]; + crate::util::metadata::side_metadata::spec_defs::VO_BIT.scan_non_zero_values::( + self.start(), + self.start() + self.size(), + &mut |address| { + use crate::util::metadata::vo_bit; + let object = vo_bit::get_object_ref_for_vo_addr(address); + objects.push(object); + }, + ); + objects + } +} + +impl RegionInspector for R { + fn region_type(&self) -> &str { + std::any::type_name::() + } + + fn start(&self) -> Address { + Region::start(self) + } + + fn size(&self) -> usize { + Self::BYTES + } +} + +use crate::vm::VMBinding; +/// SpaceAsRegion is a special RegionInspector. Some spaces do not organize its space memory with regions. +/// For those spaces, they simply return this type as the top-level region inspector so users can inspect +/// such spaces in the same way as other spaces that use regions. +pub(crate) struct SpaceAsRegion { + space: &'static dyn Space, +} + +impl SpaceAsRegion { + pub fn new(space: &'static dyn Space) -> Self { + Self { space } + } +} + +impl RegionInspector for SpaceAsRegion { + fn region_type(&self) -> &str { + std::any::type_name::>() + } + + fn start(&self) -> Address { + Address::ZERO + } + + fn size(&self) -> usize { + 0 + } + #[cfg(feature = "vo_bit")] + fn list_objects(&self) -> Vec { + let mut res = vec![]; + let mut enumerator = + crate::util::object_enum::ClosureObjectEnumerator::<_, VM>::new(|object| { + res.push(object); + }); + self.space.enumerate_objects(&mut enumerator); + res + } +} diff --git a/src/util/heap/mod.rs b/src/util/heap/mod.rs index ae974740e8..5b507cc594 100644 --- a/src/util/heap/mod.rs +++ b/src/util/heap/mod.rs @@ -22,3 +22,6 @@ pub use self::layout::vm_layout; pub(crate) use self::monotonepageresource::MonotonePageResource; pub(crate) use self::pageresource::PageResource; pub(crate) use self::vmrequest::VMRequest; + +/// Heap inspection API. +pub mod inspection; diff --git a/src/util/linear_scan.rs b/src/util/linear_scan.rs index ec39379afd..5e63dfa7a4 100644 --- a/src/util/linear_scan.rs +++ b/src/util/linear_scan.rs @@ -135,6 +135,27 @@ pub trait Region: Copy + PartialEq + PartialOrd { } } +/// Data structure to reference a MMTk 4KB page. +#[repr(transparent)] +#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Eq)] +pub struct Page(Address); + +impl Region for Page { + const LOG_BYTES: usize = crate::util::constants::LOG_BYTES_IN_PAGE as usize; + + fn from_aligned_address(address: Address) -> Self { + debug_assert!(address.is_aligned_to(Self::BYTES)); + Self(address) + } + + fn start(&self) -> Address { + self.0 + } +} + +// Re-export Chunk. +pub use crate::util::heap::chunk_map::Chunk; + /// An iterator for contiguous regions. pub struct RegionIterator { current: R, diff --git a/src/vm/tests/mock_tests/mock_test_heap_inspector_all.rs b/src/vm/tests/mock_tests/mock_test_heap_inspector_all.rs new file mode 100644 index 0000000000..608cd08e2a --- /dev/null +++ b/src/vm/tests/mock_tests/mock_test_heap_inspector_all.rs @@ -0,0 +1,83 @@ +// GITHUB-CI: MMTK_PLAN=all +// GITHUB-CI: FEATURES=vo_bit + +use std::collections::HashSet; + +use constants::BYTES_IN_WORD; + +use super::mock_test_prelude::*; + +use crate::{util::*, AllocationSemantics, MMTK}; + +lazy_static! { + static ref FIXTURE: Fixture = Fixture::new(); +} + +pub fn get_all_objects(mmtk: &'static MMTK) -> HashSet { + let mut result = HashSet::new(); + let space_inspectors = mmtk.inspect_spaces(); + assert!(space_inspectors.len() > 0); + space_inspectors.iter().for_each(|s| { + let mut regions = s.list_top_regions(); + while regions.len() > 0 { + let region = regions.pop().unwrap(); + let mut sub_regions = s.list_sub_regions(&*region); + if sub_regions.len() > 0 { + // If we have sub regions keep looking at them + regions.append(&mut sub_regions); + } else { + // Otherwise, we are at the leaf level, listing objects. + for object in region.list_objects() { + assert!(!result.contains(&object)); + result.insert(object); + } + } + } + }); + return result; +} + +#[test] +pub fn test_heap_inspector_all() { + with_mockvm( + default_setup, + || { + FIXTURE.with_fixture_mut(|fixture| { + let mmtk = fixture.mmtk(); + let mutator = &mut fixture.mutator; + + let mut new_obj = |size: usize, semantics: AllocationSemantics| { + let align = BYTES_IN_WORD; + let start = memory_manager::alloc(mutator, size, align, 0, semantics); + let object = MockVM::object_start_to_ref(start); + memory_manager::post_alloc(mutator, object, size, semantics); + object + }; + + let mut known_objects = HashSet::new(); + + let mut new_and_assert = |size: usize, semantics: AllocationSemantics| { + let object = new_obj(size, semantics); // a random size + known_objects.insert(object); + let traversal = get_all_objects(mmtk); + assert_eq!(traversal, known_objects); + }; + + { + use AllocationSemantics::*; + + // Add some single objects. Size doesn't matter. + println!("Allocating object 40"); + new_and_assert(40, Default); + println!("Allocating object 64"); + new_and_assert(64, Default); + println!("Allocating object 96"); + new_and_assert(96, Immortal); + println!("Allocating object 131000"); + new_and_assert(131000, Los); + } + }); + }, + no_cleanup, + ) +} diff --git a/src/vm/tests/mock_tests/mock_test_heap_inspector_immix.rs b/src/vm/tests/mock_tests/mock_test_heap_inspector_immix.rs new file mode 100644 index 0000000000..66557b6cce --- /dev/null +++ b/src/vm/tests/mock_tests/mock_test_heap_inspector_immix.rs @@ -0,0 +1,90 @@ +// GITHUB-CI: MMTK_PLAN=Immix +// GITHUB-CI: FEATURES=vo_bit + +use constants::BYTES_IN_WORD; + +use super::mock_test_prelude::*; + +use crate::{util::*, AllocationSemantics}; + +lazy_static! { + static ref FIXTURE: Fixture = Fixture::new(); +} + +#[test] +pub fn test_heap_inspector_immix() { + with_mockvm( + default_setup, + || { + FIXTURE.with_fixture_mut(|fixture| { + let mmtk = fixture.mmtk(); + let mutator = &mut fixture.mutator; + let space_inspector = mmtk.inspect_spaces(); + assert!(space_inspector.len() > 0); + + let get_immix_inspector = || { + space_inspector.iter().find(|s| s.space_name() == "immix").unwrap() + }; + + { + let immix_space_inspector = get_immix_inspector(); + assert_eq!(immix_space_inspector.policy_name(), "mmtk::policy::immix::immixspace::ImmixSpace"); + let chunk_inspector = immix_space_inspector.list_top_regions(); + assert_eq!(chunk_inspector.len(), 0); + } + + let mut new_obj = |size: usize, semantics: AllocationSemantics| { + let align = BYTES_IN_WORD; + let start = memory_manager::alloc(mutator, size, align, 0, semantics); + let object = MockVM::object_start_to_ref(start); + memory_manager::post_alloc(mutator, object, size, semantics); + object + }; + + // Allocate one object + let object = new_obj(40, AllocationSemantics::Default); + + { + let immix_space_inspector = get_immix_inspector(); + // Check chunks + let chunk_inspector = immix_space_inspector.list_top_regions(); + assert_eq!(chunk_inspector.len(), 1); + assert_eq!(chunk_inspector[0].region_type(), "mmtk::util::heap::chunk_map::Chunk"); + let objects = chunk_inspector[0].list_objects(); + assert_eq!(objects.len(), 1); + assert_eq!(objects[0], object); + // Check blocks + let block_inspector = immix_space_inspector.list_sub_regions(&*chunk_inspector[0]); + assert_eq!(block_inspector.len(), 128); // 128 blocks in a chunk + assert_eq!(block_inspector[0].region_type(), "mmtk::policy::immix::block::Block"); + let objects = block_inspector[0].list_objects(); + assert_eq!(objects.len(), 1); + assert_eq!(objects[0], object); + // Check lines + let line_inspector = immix_space_inspector.list_sub_regions(&*block_inspector[0]); + assert_eq!(line_inspector.len(), 128); // 128 lines in a block + assert_eq!(line_inspector[0].region_type(), "mmtk::policy::immix::line::Line"); + let objects = line_inspector[0].list_objects(); + assert_eq!(objects.len(), 1); + assert_eq!(objects[0], object); + } + + // Allocate another object + let object2 = new_obj(40, AllocationSemantics::Default); + + { + let immix_space_inspector = get_immix_inspector(); + // Check checks + let chunk_inspector = immix_space_inspector.list_top_regions(); + assert_eq!(chunk_inspector.len(), 1); + assert_eq!(chunk_inspector[0].region_type(), "mmtk::util::heap::chunk_map::Chunk"); + let objects = chunk_inspector[0].list_objects(); + assert_eq!(objects.len(), 2); + assert_eq!(objects[0], object); + assert_eq!(objects[1], object2); + } + }); + }, + no_cleanup, + ) +} diff --git a/src/vm/tests/mock_tests/mod.rs b/src/vm/tests/mock_tests/mod.rs index 946d861da1..15d7eec32a 100644 --- a/src/vm/tests/mock_tests/mod.rs +++ b/src/vm/tests/mock_tests/mod.rs @@ -39,6 +39,10 @@ mod mock_test_conservatism; mod mock_test_handle_mmap_conflict; mod mock_test_handle_mmap_oom; #[cfg(feature = "vo_bit")] +mod mock_test_heap_inspector_all; +#[cfg(feature = "vo_bit")] +mod mock_test_heap_inspector_immix; +#[cfg(feature = "vo_bit")] mod mock_test_heap_traversal; mod mock_test_init_fork; #[cfg(feature = "is_mmtk_object")]