-
Notifications
You must be signed in to change notification settings - Fork 78
Introduce SpaceInspector and RegionInspector #1322
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -167,6 +167,9 @@ impl<VM: VMBinding> Space<VM> for ImmixSpace<VM> { | |
fn as_sft(&self) -> &(dyn SFT + Sync + 'static) { | ||
self | ||
} | ||
fn as_inspector(&self) -> Option<&dyn crate::util::heap::inspection::SpaceInspector> { | ||
Some(self) | ||
} | ||
fn get_page_resource(&self) -> &dyn PageResource<VM> { | ||
&self.pr | ||
} | ||
|
@@ -409,7 +412,7 @@ impl<VM: VMBinding> ImmixSpace<VM> { | |
if self.common.needs_log_bit { | ||
if let MetadataSpec::OnSide(side) = *VM::VMObjectModel::GLOBAL_LOG_BIT_SPEC { | ||
for chunk in self.chunk_map.all_chunks() { | ||
side.bzero_metadata(chunk.start(), Chunk::BYTES); | ||
side.bzero_metadata(chunk .start(), Chunk::BYTES); | ||
} | ||
} | ||
} | ||
|
@@ -1178,3 +1181,27 @@ impl ClearVOBitsAfterPrepare { | |
} | ||
} | ||
} | ||
|
||
mod inspector { | ||
use super::*; | ||
use crate::util::heap::inspection::{RegionInspector, SpaceInspector, list_child_regions}; | ||
impl<VM: VMBinding> SpaceInspector for ImmixSpace<VM> { | ||
fn name(&self) -> &str { | ||
SFT::name(self) | ||
} | ||
|
||
fn list_regions(&self, parent_region: Option<&dyn RegionInspector>) -> Vec<Box<dyn RegionInspector>> { | ||
if let Some(parent_region) = parent_region { | ||
list_child_regions::<Chunk, Block>(parent_region).or_else(|| { | ||
if !crate::policy::immix::BLOCK_ONLY { | ||
list_child_regions::<Block, Line>(parent_region) | ||
} else { | ||
None | ||
} | ||
}).unwrap_or_else(|| vec![]) | ||
} else { | ||
self.chunk_map.all_chunks().map(|r: Chunk| Box::new(r) as Box<dyn RegionInspector>).collect() | ||
} | ||
} | ||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,9 @@ use downcast_rs::Downcast; | |
pub trait Space<VM: VMBinding>: 'static + SFT + Sync + Downcast { | ||
fn as_space(&self) -> &dyn Space<VM>; | ||
fn as_sft(&self) -> &(dyn SFT + Sync + 'static); | ||
fn as_inspector(&self) -> Option<&dyn SpaceInspector> { | ||
|
||
None | ||
} | ||
fn get_page_resource(&self) -> &dyn PageResource<VM>; | ||
|
||
/// Get a mutable reference to the underlying page resource, or `None` if the space does not | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
use crate::policy::sft::SFT; | ||
use crate::util::linear_scan::RegionIterator; | ||
use crate::util::{linear_scan::Region, Address, ObjectReference}; | ||
use crate::util::metadata::side_metadata::spec_defs::VO_BIT; | ||
use crate::util::heap::chunk_map::Chunk; | ||
|
||
pub trait SpaceInspector { | ||
fn name(&self) -> &str; | ||
fn list_regions(&self, parent_region: Option<&dyn RegionInspector>) -> Vec<Box<dyn RegionInspector>>; | ||
|
||
} | ||
|
||
pub(crate) fn list_child_regions<PARENT: Region, CHILD: Region + 'static>(region: &dyn RegionInspector) -> Option<Vec<Box<dyn RegionInspector>>> { | ||
if region.region_type() == std::any::type_name::<PARENT>() { | ||
|
||
let start_child_region = CHILD::from_aligned_address(region.start()); | ||
let end_child_region = CHILD::from_aligned_address(region.start() + region.size()); | ||
Some(RegionIterator::<CHILD>::new(start_child_region, end_child_region) | ||
.map(|r| Box::new(r) as Box<dyn RegionInspector>) | ||
.collect()) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
pub trait RegionInspector { | ||
fn region_type(&self) -> &str; | ||
|
||
fn start(&self) -> Address; | ||
fn size(&self) -> usize; | ||
#[cfg(feature = "vo_bit")] | ||
fn list_objects(&self) -> Vec<ObjectReference> { | ||
let mut objects = vec![]; | ||
VO_BIT.scan_non_zero_values::<u8>(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<R: Region> RegionInspector for R { | ||
fn region_type(&self) -> &str { | ||
std::any::type_name::<R>() | ||
} | ||
|
||
fn start(&self) -> Address { | ||
Region::start(self) | ||
} | ||
|
||
fn size(&self) -> usize { | ||
Self::BYTES | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// GITHUB-CI: MMTK_PLAN=Immix | ||
// 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<MutatorFixture> = Fixture::new(); | ||
} | ||
|
||
pub fn get_all_objects(mmtk: &'static MMTK<MockVM>) -> HashSet<ObjectReference> { | ||
let mut result = HashSet::new(); | ||
mmtk.enumerate_objects(|object| { | ||
result.insert(object); | ||
}); | ||
result | ||
} | ||
|
||
#[test] | ||
pub fn test_heap_inspector() { | ||
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.name() == "immix").unwrap() | ||
}; | ||
|
||
{ | ||
let immix_space_inspector = get_immix_inspector(); | ||
let chunk_inspector = immix_space_inspector.list_regions(None); | ||
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_regions(None); | ||
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_regions(Some(&*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_regions(Some(&*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_regions(None); | ||
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, | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Having a
Vec
does simplify the API a bit because it can be iterated, and the performance doesn't matter because (1) we are inspecting, and (2) there are probably not may spaces in a plan.