From e373275d1bfec2514854f327b43ca494a66fd28d Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Mon, 14 Apr 2025 01:42:22 +0000 Subject: [PATCH] Object enumeration also returns the space name and the region --- .github/scripts/ci-test.sh | 2 +- src/mmtk.rs | 11 +-- src/util/mod.rs | 1 + src/util/object_enum.rs | 67 ++++++++++++++++--- .../mock_test_doc_mutator_storage.rs | 3 +- .../mock_tests/mock_test_heap_traversal.rs | 7 +- 6 files changed, 71 insertions(+), 20 deletions(-) diff --git a/.github/scripts/ci-test.sh b/.github/scripts/ci-test.sh index c25a22ec7c..19c54b66b7 100755 --- a/.github/scripts/ci-test.sh +++ b/.github/scripts/ci-test.sh @@ -22,7 +22,7 @@ find ./src ./tests -type f -name "mock_test_*" | while read -r file; do # Get the required plans. # Some tests need to be run with multiple plans because # some bugs can only be reproduced in some plans but not others. - PLANS=$(sed -n 's/^\/\/ *GITHUB-CI: *MMTK_PLAN=//p' $file) + PLANS=$(sed -n 's/^\/\/ *GITHUB-CI: *MMTK_PLAN=//p' $file | tr ',' '\n') if [[ $PLANS == 'all' ]]; then PLANS=$ALL_PLANS elif [[ -z $PLANS ]]; then diff --git a/src/mmtk.rs b/src/mmtk.rs index e0a355108f..1b18b7564d 100644 --- a/src/mmtk.rs +++ b/src/mmtk.rs @@ -6,8 +6,6 @@ use crate::plan::Plan; use crate::policy::sft_map::{create_sft_map, SFTMap}; use crate::scheduler::GCWorkScheduler; -#[cfg(feature = "vo_bit")] -use crate::util::address::ObjectReference; #[cfg(feature = "analysis")] use crate::util::analysis::AnalysisManager; use crate::util::finalizable_processor::FinalizableProcessor; @@ -24,6 +22,8 @@ use crate::util::sanity::sanity_checker::SanityChecker; #[cfg(feature = "extreme_assertions")] use crate::util::slot_logger::SlotLogger; use crate::util::statistics::stats::Stats; +#[cfg(feature = "vo_bit")] +use crate::util::EnumeratedObject; use crate::vm::ReferenceGlue; use crate::vm::VMBinding; use std::cell::UnsafeCell; @@ -516,15 +516,16 @@ impl MMTK { /// /// [os_eo]: https://docs.ruby-lang.org/en/master/ObjectSpace.html#method-c-each_object #[cfg(feature = "vo_bit")] - pub fn enumerate_objects(&self, f: F) + pub fn enumerate_objects(&self, mut f: F) where - F: FnMut(ObjectReference), + F: FnMut(EnumeratedObject), { use crate::util::object_enum; - let mut enumerator = object_enum::ClosureObjectEnumerator::<_, VM>::new(f); let plan = self.get_plan(); plan.for_each_space(&mut |space| { + let mut enumerator = + object_enum::ClosureObjectEnumerator::<_, VM>::new(space.get_name(), &mut f); space.enumerate_objects(&mut enumerator); }) } diff --git a/src/util/mod.rs b/src/util/mod.rs index d22c29a2e3..140c93fee0 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -83,4 +83,5 @@ mod raw_memory_freelist; pub use self::address::Address; pub use self::address::ObjectReference; +pub use self::object_enum::EnumeratedObject; pub use self::opaque_pointer::*; diff --git a/src/util/object_enum.rs b/src/util/object_enum.rs index 18508f088b..9b9dce54c8 100644 --- a/src/util/object_enum.rs +++ b/src/util/object_enum.rs @@ -14,6 +14,29 @@ use super::{ Address, ObjectReference, }; +/// The object that gets enumerated. +#[derive(PartialEq, Eq, Hash, Copy, Clone)] +pub enum EnumeratedObject<'a> { + /// A single object + Single { + /// The name of the space where the object is located + space_name: &'a str, + /// The reference to the object + reference: ObjectReference, + }, + /// An object in a block. Blocks are memory regions with fixed sizes. + InBlock { + /// The name of the space where the object is located + space_name: &'a str, + /// The reference to the object + reference: ObjectReference, + /// The start address of the block + block_start: Address, + /// The size of the block in bytes + block_size: usize, + }, +} + /// A trait for enumerating objects in spaces, used by [`Space::enumerate_objects`]. /// /// [`Space::enumerate_objects`]: crate::policy::space::Space::enumerate_objects @@ -22,44 +45,66 @@ pub trait ObjectEnumerator { fn visit_object(&mut self, object: ObjectReference); /// Visit an address range that may contain objects. fn visit_address_range(&mut self, start: Address, end: Address); + /// Visit a block of memory that may contain objects. Blocks are address ranges with a fixed size. + fn visit_block(&mut self, block_start: Address, block_size: usize); } /// An implementation of `ObjectEnumerator` that wraps a callback. -pub struct ClosureObjectEnumerator +pub struct ClosureObjectEnumerator<'a, F, VM> where - F: FnMut(ObjectReference), + F: FnMut(EnumeratedObject), VM: VMBinding, { - object_callback: F, + space_name: &'a str, + object_callback: &'a mut F, phantom_data: PhantomData, } -impl ClosureObjectEnumerator +impl<'a, F, VM> ClosureObjectEnumerator<'a, F, VM> where - F: FnMut(ObjectReference), + F: FnMut(EnumeratedObject), VM: VMBinding, { - pub fn new(object_callback: F) -> Self { + pub fn new(space_name: &'a str, object_callback: &'a mut F) -> Self { Self { + space_name, object_callback, phantom_data: PhantomData, } } } -impl ObjectEnumerator for ClosureObjectEnumerator +impl ObjectEnumerator for ClosureObjectEnumerator<'_, F, VM> where - F: FnMut(ObjectReference), + F: FnMut(EnumeratedObject), VM: VMBinding, { fn visit_object(&mut self, object: ObjectReference) { - (self.object_callback)(object); + (self.object_callback)(EnumeratedObject::Single { + space_name: self.space_name, + reference: object, + }); } fn visit_address_range(&mut self, start: Address, end: Address) { VO_BIT.scan_non_zero_values::(start, end, &mut |address| { let object = vo_bit::get_object_ref_for_vo_addr(address); - (self.object_callback)(object); + (self.object_callback)(EnumeratedObject::Single { + space_name: self.space_name, + reference: object, + }); + }) + } + + fn visit_block(&mut self, block_start: Address, block_size: usize) { + VO_BIT.scan_non_zero_values::(block_start, block_start + block_size, &mut |address| { + let object = vo_bit::get_object_ref_for_vo_addr(address); + (self.object_callback)(EnumeratedObject::InBlock { + space_name: self.space_name, + reference: object, + block_start, + block_size, + }); }) } } @@ -88,7 +133,7 @@ pub(crate) fn enumerate_blocks_from_chunk_map( if chunk_map.get(chunk) == ChunkState::Allocated { for block in chunk.iter_region::() { if block.may_have_objects() { - enumerator.visit_address_range(block.start(), block.end()); + enumerator.visit_block(block.start(), B::BYTES); } } } diff --git a/src/vm/tests/mock_tests/mock_test_doc_mutator_storage.rs b/src/vm/tests/mock_tests/mock_test_doc_mutator_storage.rs index a79ef25a55..4590ebf983 100644 --- a/src/vm/tests/mock_tests/mock_test_doc_mutator_storage.rs +++ b/src/vm/tests/mock_tests/mock_test_doc_mutator_storage.rs @@ -1,4 +1,5 @@ -// GITHUB-CI: MMTK_PLAN=NoGC,SemiSpace,Immix,GenImmix,StickyImmix +// GITHUB-CI: MMTK_PLAN=NoGC,SemiSpace,GenImmix +// The test assumes the default allocator is a bump pointer allocator, thus only works for plans that use BumpAlloator (not ImmixAllocator). use super::mock_test_prelude::*; diff --git a/src/vm/tests/mock_tests/mock_test_heap_traversal.rs b/src/vm/tests/mock_tests/mock_test_heap_traversal.rs index 430c46ff2a..f800286397 100644 --- a/src/vm/tests/mock_tests/mock_test_heap_traversal.rs +++ b/src/vm/tests/mock_tests/mock_test_heap_traversal.rs @@ -21,8 +21,11 @@ lazy_static! { pub fn get_all_objects(mmtk: &'static MMTK) -> HashSet { let mut result = HashSet::new(); - mmtk.enumerate_objects(|object| { - result.insert(object); + mmtk.enumerate_objects(|object: EnumeratedObject| { + result.insert(match object { + EnumeratedObject::Single { reference, .. } => reference, + EnumeratedObject::InBlock { reference, .. } => reference, + }); }); result }