mmtk/
mmtk.rs

1//! MMTk instance.
2use crate::global_state::{GcStatus, GlobalState};
3use crate::plan::CreateGeneralPlanArgs;
4use crate::plan::Plan;
5use crate::policy::sft_map::{create_sft_map, SFTMap};
6use crate::scheduler::GCWorkScheduler;
7
8#[cfg(feature = "vo_bit")]
9use crate::util::address::ObjectReference;
10#[cfg(feature = "analysis")]
11use crate::util::analysis::AnalysisManager;
12use crate::util::finalizable_processor::FinalizableProcessor;
13use crate::util::heap::gc_trigger::GCTrigger;
14use crate::util::heap::layout::heap_parameters::MAX_SPACES;
15use crate::util::heap::layout::vm_layout::{vm_layout, VMLayout};
16use crate::util::heap::layout::{self, Mmapper, VMMap};
17use crate::util::heap::HeapMeta;
18use crate::util::opaque_pointer::*;
19use crate::util::options::Options;
20use crate::util::reference_processor::ReferenceProcessors;
21#[cfg(feature = "sanity")]
22use crate::util::sanity::sanity_checker::SanityChecker;
23#[cfg(feature = "extreme_assertions")]
24use crate::util::slot_logger::SlotLogger;
25use crate::util::statistics::stats::Stats;
26#[cfg(feature = "vm_space")]
27use crate::vm::object_model::ObjectModel;
28use crate::vm::ReferenceGlue;
29use crate::vm::VMBinding;
30use std::cell::UnsafeCell;
31use std::collections::HashMap;
32use std::default::Default;
33#[cfg(feature = "sanity")]
34use std::sync::atomic::AtomicBool;
35use std::sync::atomic::Ordering;
36use std::sync::Arc;
37use std::sync::Mutex;
38
39lazy_static! {
40    // I am not sure if we should include these mmappers as part of MMTk struct.
41    // The considerations are:
42    // 1. We need VMMap and Mmapper to create spaces. It is natural that the mappers are not
43    //    part of MMTK, as creating MMTK requires these mappers. We could use Rc/Arc for these mappers though.
44    // 2. These mmappers are possibly global across multiple MMTk instances, as they manage the
45    //    entire address space.
46    // TODO: We should refactor this when we know more about how multiple MMTK instances work.
47
48    /// A global VMMap that manages the mapping of spaces to virtual memory ranges.
49    pub static ref VM_MAP: Box<dyn VMMap + Send + Sync> = layout::create_vm_map();
50
51    /// A global Mmapper for mmaping and protection of virtual memory.
52    pub static ref MMAPPER: Box<dyn Mmapper> = layout::create_mmapper();
53}
54
55use crate::util::rust_util::InitializeOnce;
56
57// A global space function table that allows efficient dispatch space specific code for addresses in our heap.
58pub static SFT_MAP: InitializeOnce<Box<dyn SFTMap>> = InitializeOnce::new();
59
60/// MMTk builder. This is used to set options and other settings before actually creating an MMTk instance.
61pub struct MMTKBuilder {
62    /// The options for this instance.
63    pub options: Options,
64}
65
66impl MMTKBuilder {
67    /// Create an MMTK builder with options read from environment variables, or using built-in
68    /// default if not overridden by environment variables.
69    pub fn new() -> Self {
70        let mut builder = Self::new_no_env_vars();
71        builder.options.read_env_var_settings();
72        builder
73    }
74
75    /// Create an MMTK builder with build-in default options, but without reading options from
76    /// environment variables.
77    pub fn new_no_env_vars() -> Self {
78        MMTKBuilder {
79            options: Options::default(),
80        }
81    }
82
83    /// Set an option.
84    pub fn set_option(&mut self, name: &str, val: &str) -> bool {
85        self.options.set_from_string(name, val)
86    }
87
88    /// Set multiple options by a string. The string should be key-value pairs separated by white spaces,
89    /// such as `threads=1 stress_factor=4096`.
90    pub fn set_options_bulk_by_str(&mut self, options: &str) -> bool {
91        self.options.set_bulk_from_string(options)
92    }
93
94    /// Custom VM layout constants. VM bindings may use this function for compressed or 39-bit heap support.
95    /// This function must be called before MMTk::new()
96    pub fn set_vm_layout(&mut self, constants: VMLayout) {
97        VMLayout::set_custom_vm_layout(constants)
98    }
99
100    /// Build an MMTk instance from the builder.
101    pub fn build<VM: VMBinding>(&self) -> MMTK<VM> {
102        let mut options = self.options.clone();
103        options.resolve_connected_options();
104        MMTK::new(Arc::new(options))
105    }
106}
107
108impl Default for MMTKBuilder {
109    fn default() -> Self {
110        Self::new()
111    }
112}
113
114/// An MMTk instance. MMTk allows multiple instances to run independently, and each instance gives users a separate heap.
115/// *Note that multi-instances is not fully supported yet*
116pub struct MMTK<VM: VMBinding> {
117    pub(crate) options: Arc<Options>,
118    pub(crate) state: Arc<GlobalState>,
119    pub(crate) plan: UnsafeCell<Box<dyn Plan<VM = VM>>>,
120    pub(crate) reference_processors: ReferenceProcessors,
121    pub(crate) finalizable_processor:
122        Mutex<FinalizableProcessor<<VM::VMReferenceGlue as ReferenceGlue<VM>>::FinalizableType>>,
123    pub(crate) scheduler: Arc<GCWorkScheduler<VM>>,
124    #[cfg(feature = "sanity")]
125    pub(crate) sanity_checker: Mutex<SanityChecker<VM::VMSlot>>,
126    #[cfg(feature = "extreme_assertions")]
127    pub(crate) slot_logger: SlotLogger<VM::VMSlot>,
128    pub(crate) gc_trigger: Arc<GCTrigger<VM>>,
129    pub(crate) stats: Arc<Stats>,
130    #[cfg(feature = "sanity")]
131    inside_sanity: AtomicBool,
132    /// Analysis counters. The feature analysis allows us to periodically stop the world and collect some statistics.
133    #[cfg(feature = "analysis")]
134    pub(crate) analysis_manager: Arc<AnalysisManager<VM>>,
135}
136
137unsafe impl<VM: VMBinding> Sync for MMTK<VM> {}
138unsafe impl<VM: VMBinding> Send for MMTK<VM> {}
139
140impl<VM: VMBinding> MMTK<VM> {
141    /// Create an MMTK instance. This is not public. Bindings should use [`MMTKBuilder::build`].
142    pub(crate) fn new(options: Arc<Options>) -> Self {
143        // Verify the Mmapper can handle the required address space size.
144        vm_layout().validate_address_space();
145
146        // Initialize SFT first in case we need to use this in the constructor.
147        // The first call will initialize SFT map. Other calls will be blocked until SFT map is initialized.
148        crate::policy::sft_map::SFTRefStorage::pre_use_check();
149        SFT_MAP.initialize_once(&create_sft_map);
150
151        let num_workers = if cfg!(feature = "single_worker") {
152            1
153        } else {
154            *options.threads
155        };
156
157        let scheduler = GCWorkScheduler::new(num_workers, (*options.thread_affinity).clone());
158
159        let state = Arc::new(GlobalState::default());
160
161        let gc_trigger = Arc::new(GCTrigger::new(
162            options.clone(),
163            scheduler.clone(),
164            state.clone(),
165        ));
166
167        let stats = Arc::new(Stats::new(&options));
168
169        // We need this during creating spaces, but we do not use this once the MMTk instance is created.
170        // So we do not save it in MMTK. This may change in the future.
171        let mut heap = HeapMeta::new();
172
173        // Create plan and spaces. Note that side metadata is not initialized yet. Plan creation should avoid using it.
174        let mut plan = crate::plan::create_plan(
175            *options.plan,
176            CreateGeneralPlanArgs {
177                vm_map: VM_MAP.as_ref(),
178                mmapper: MMAPPER.as_ref(),
179                options: options.clone(),
180                state: state.clone(),
181                gc_trigger: gc_trigger.clone(),
182                scheduler: scheduler.clone(),
183                stats: &stats,
184                heap: &mut heap,
185            },
186        );
187
188        // Initialize side metadata runtime state and reserve its address range after creating spaces.
189        crate::util::metadata::side_metadata::initialize_side_metadata::<VM>(&options);
190
191        // We haven't finished creating MMTk. No one is using the GC trigger. We cast the arc into a mutable reference.
192        {
193            // TODO: use Arc::get_mut_unchecked() when it is availble.
194            let gc_trigger: &mut GCTrigger<VM> =
195                unsafe { &mut *(Arc::as_ptr(&gc_trigger) as *mut _) };
196            // We know the plan address will not change. Cast it to a static reference.
197            let static_plan: &'static dyn Plan<VM = VM> = unsafe { &*(&*plan as *const _) };
198            // Set the plan so we can trigger GC and check GC condition without using plan
199            gc_trigger.set_plan(static_plan);
200        }
201
202        // TODO: This probably does not work if we have multiple MMTk instances.
203        // This needs to be called after we create Plan. It needs to use HeapMeta, which is gradually built when we create spaces.
204        VM_MAP.finalize_static_space_map(
205            heap.get_discontig_start(),
206            heap.get_discontig_end(),
207            &mut |start_address| {
208                plan.for_each_space_mut(&mut |space| {
209                    // If the `VMMap` has a discontiguous memory range, we notify all discontiguous
210                    // space that the starting address has been determined.
211                    if let Some(pr) = space.maybe_get_page_resource_mut() {
212                        pr.update_discontiguous_start(start_address);
213                    }
214                })
215            },
216        );
217
218        // The order here is important:
219        plan.initialize_side_metadata();
220        // Initialize side metadat sanity first
221        plan.verify_side_metadata_sanity();
222        // Then intiialize SFT because it may use side metadata
223        plan.initialize_sft();
224
225        MMTK {
226            options,
227            state,
228            plan: UnsafeCell::new(plan),
229            reference_processors: ReferenceProcessors::new(),
230            finalizable_processor: Mutex::new(FinalizableProcessor::<
231                <VM::VMReferenceGlue as ReferenceGlue<VM>>::FinalizableType,
232            >::new()),
233            scheduler,
234            #[cfg(feature = "sanity")]
235            sanity_checker: Mutex::new(SanityChecker::new()),
236            #[cfg(feature = "sanity")]
237            inside_sanity: AtomicBool::new(false),
238            #[cfg(feature = "extreme_assertions")]
239            slot_logger: SlotLogger::new(),
240            #[cfg(feature = "analysis")]
241            analysis_manager: Arc::new(AnalysisManager::new(stats.clone())),
242            gc_trigger,
243            stats,
244        }
245    }
246
247    /// Initialize the GC worker threads that are required for doing garbage collections.
248    /// This is a mandatory call for a VM during its boot process once its thread system
249    /// is ready.
250    ///
251    /// Internally, this function will invoke [`Collection::spawn_gc_thread()`] to spawn GC worker
252    /// threads.
253    ///
254    /// # Arguments
255    ///
256    /// *   `tls`: The thread that wants to enable the collection. This value will be passed back
257    ///     to the VM in [`Collection::spawn_gc_thread()`] so that the VM knows the context.
258    ///
259    /// [`Collection::spawn_gc_thread()`]: crate::vm::Collection::spawn_gc_thread()
260    pub fn initialize_collection(&'static self, tls: VMThread) {
261        assert!(
262            !self.state.is_initialized(),
263            "MMTk collection has been initialized (was initialize_collection() already called before?)"
264        );
265        self.scheduler.spawn_gc_threads(self, tls);
266        self.state.gc_status.set_initialized();
267        probe!(mmtk, collection_initialized);
268    }
269
270    /// Shut down all GC worker threads.
271    pub fn shutdown(&'static self) {
272        if self.state.is_initialized() {
273            self.scheduler.shutdown_gc_threads();
274            self.state.gc_status.set_uninitialized();
275        }
276    }
277
278    /// Prepare an MMTk instance for calling the `fork()` system call.
279    ///
280    /// The `fork()` system call is available on Linux and some UNIX variants, and may be emulated
281    /// on other platforms by libraries such as Cygwin.  The properties of the `fork()` system call
282    /// requires the users to do some preparation before calling it.
283    ///
284    /// -   **Multi-threading**:  If `fork()` is called when the process has multiple threads, it
285    ///     will only duplicate the current thread into the child process, and the child process can
286    ///     only call async-signal-safe functions, notably `exec()`.  For VMs that that use
287    ///     multi-process concurrency, it is imperative that when calling `fork()`, only one thread may
288    ///     exist in the process.
289    ///
290    /// -   **File descriptors**: The child process inherits copies of the parent's set of open
291    ///     file descriptors.  This may or may not be desired depending on use cases.
292    ///
293    /// This function helps VMs that use `fork()` for multi-process concurrency.  It instructs all
294    /// GC threads to save their contexts and return from their entry-point functions.  Currently,
295    /// such threads only include GC workers, and the entry point is
296    /// [`crate::memory_manager::start_worker`].  A subsequent call to `MMTK::after_fork()` will
297    /// re-spawn the threads using their saved contexts.  The VM must not allocate objects in the
298    /// MMTk heap before calling `MMTK::after_fork()`.
299    ///
300    /// TODO: Currently, the MMTk core does not keep any files open for a long time.  In the
301    /// future, this function and the `after_fork` function may be used for handling open file
302    /// descriptors across invocations of `fork()`.  One possible use case is logging GC activities
303    /// and statistics to files, such as performing heap dumps across multiple GCs.
304    ///
305    /// If a VM intends to execute another program by calling `fork()` and immediately calling
306    /// `exec`, it may skip this function because the state of the MMTk instance will be irrelevant
307    /// in that case.
308    ///
309    /// # Caution!
310    ///
311    /// This function sends an asynchronous message to GC threads and returns immediately, but it
312    /// is only safe for the VM to call `fork()` after the underlying **native threads** of the GC
313    /// threads have exited.  After calling this function, the VM should wait for their underlying
314    /// native threads to exit in VM-specific manner before calling `fork()`.
315    pub fn prepare_to_fork(&'static self) {
316        assert!(
317            self.state.is_initialized(),
318            "MMTk collection has not been initialized, yet (was initialize_collection() called before?)"
319        );
320        probe!(mmtk, prepare_to_fork);
321        self.scheduler.stop_gc_threads_for_forking();
322    }
323
324    /// Call this function after the VM called the `fork()` system call.
325    ///
326    /// This function will re-spawn MMTk threads from saved contexts.
327    ///
328    /// # Arguments
329    ///
330    /// *   `tls`: The thread that wants to respawn MMTk threads after forking. This value will be
331    ///     passed back to the VM in `Collection::spawn_gc_thread()` so that the VM knows the
332    ///     context.
333    pub fn after_fork(&'static self, tls: VMThread) {
334        assert!(
335            self.state.is_initialized(),
336            "MMTk collection has not been initialized, yet (was initialize_collection() called before?)"
337        );
338        probe!(mmtk, after_fork);
339        self.scheduler.respawn_gc_threads_after_forking(tls);
340    }
341
342    /// Generic hook to allow benchmarks to be harnessed. MMTk will trigger a GC
343    /// to clear any residual garbage and start collecting statistics for the benchmark.
344    /// This is usually called by the benchmark harness as its last step before the actual benchmark.
345    pub fn harness_begin(&self, tls: VMMutatorThread) {
346        probe!(mmtk, harness_begin);
347        let gc_triggered = self.handle_user_collection_request(tls, true, true);
348        // Since handle_user_collection_request may not trigger GC if tls is null, we add a
349        // block_for_gc to compensate for this because we force a GC in harness begin.
350        //
351        // Only do this if a GC was actually triggered. A plan that does not collect garbage
352        // (NoGC) ignores the request and never schedules a GC, so blocking here would wait
353        // forever and deadlock the VM.
354        //
355        // FIXME: Fix the API of handle_user_collection_request so that we won't need this
356        // workaround.
357        if gc_triggered && tls.0 .0.is_null() {
358            use crate::vm::Collection;
359            VM::VMCollection::block_for_gc(tls);
360        }
361        self.state.inside_harness.store(true, Ordering::SeqCst);
362        self.stats.start_all();
363        self.scheduler.enable_stat();
364    }
365
366    /// Generic hook to allow benchmarks to be harnessed. MMTk will stop collecting
367    /// statistics, and print out the collected statistics in a defined format.
368    /// This is usually called by the benchmark harness right after the actual benchmark.
369    pub fn harness_end(&'static self) {
370        self.stats.stop_all(self);
371        self.state.inside_harness.store(false, Ordering::SeqCst);
372        probe!(mmtk, harness_end);
373    }
374
375    #[cfg(feature = "sanity")]
376    pub(crate) fn sanity_begin(&self) {
377        self.inside_sanity.store(true, Ordering::Relaxed)
378    }
379
380    #[cfg(feature = "sanity")]
381    pub(crate) fn sanity_end(&self) {
382        self.inside_sanity.store(false, Ordering::Relaxed)
383    }
384
385    #[cfg(feature = "sanity")]
386    #[allow(unused)]
387    pub(crate) fn is_in_sanity(&self) -> bool {
388        self.inside_sanity.load(Ordering::Relaxed)
389    }
390
391    /// Get the current GC status for MMTk.
392    pub fn get_gc_status(&self) -> GcStatus {
393        self.state.gc_status.load()
394    }
395
396    /// Disable collection. On success, returns `Ok(true)` if this call actually switched
397    /// collection from enabled to disabled, `Ok(false)` if it only increased the nesting depth of
398    /// an already-disabled status. If MMTk is unable to disable GC right now (possibly a GC is in
399    /// progress, or a GC has been requested), returns `Err` with the status that prevented it;
400    /// users should invoke runtime safepoints or other mechanisms to prepare for a GC pause, and
401    /// then call this function again.
402    ///
403    /// This call is nestable. Each call must be paired with a matching call to
404    /// [`MMTK::enable_collection`].
405    pub fn disable_collection(&self) -> Result<bool, GcStatus> {
406        self.gc_trigger.disable_collection()
407    }
408
409    /// Enable collection. If collection is not currently disabled (e.g. there was no prior
410    /// matching call to [`MMTK::disable_collection`]), this is a no-op.
411    /// Returns `true` if this call actually re-enabled collection (i.e. it was the outermost
412    /// matching call), `false` if it only decremented the nesting depth, or if collection was
413    /// already enabled.
414    pub fn enable_collection(&self) -> bool {
415        self.gc_trigger.enable_collection()
416    }
417
418    /// Return whether collection is currently enabled.
419    pub fn is_collection_enabled(&self) -> bool {
420        self.gc_trigger.is_collection_enabled()
421    }
422
423    /// Return true if the current GC is an emergency GC.
424    ///
425    /// An emergency GC happens when a normal GC cannot reclaim enough memory to satisfy allocation
426    /// requests.  Plans may do full-heap GC, defragmentation, etc. during emergency GCs in order to
427    /// free up more memory.
428    ///
429    /// VM bindings can call this function during GC to check if the current GC is an emergency GC.
430    /// If it is, the VM binding is recommended to retain fewer objects than normal GCs, to the
431    /// extent allowed by the specification of the VM or the language.  For example, the VM binding
432    /// may choose not to retain objects used for caching.  Specifically, for Java virtual machines,
433    /// that means not retaining referents of [`SoftReference`][java-soft-ref] which is primarily
434    /// designed for implementing memory-sensitive caches.
435    ///
436    /// [java-soft-ref]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/ref/SoftReference.html
437    pub fn is_emergency_collection(&self) -> bool {
438        self.state.is_emergency_collection()
439    }
440
441    /// Return true if the current GC is trigger manually by the user/binding.
442    pub fn is_user_triggered_collection(&self) -> bool {
443        self.state.is_user_triggered_collection()
444    }
445
446    /// The application code has requested a collection. This is just a GC hint, and
447    /// we may ignore it.
448    ///
449    /// Returns whether a GC was ran or not. If MMTk triggers a GC, this method will block the
450    /// calling thread and return true when the GC finishes. Otherwise, this method returns
451    /// false immediately.
452    ///
453    /// # Arguments
454    /// * `tls`: The mutator thread that requests the GC
455    /// * `force`: The request cannot be ignored (except for NoGC)
456    /// * `exhaustive`: The requested GC should be exhaustive. This is also a hint.
457    pub fn handle_user_collection_request(
458        &self,
459        tls: VMMutatorThread,
460        force: bool,
461        exhaustive: bool,
462    ) -> bool {
463        if self
464            .gc_trigger
465            .handle_user_collection_request(force, exhaustive)
466        {
467            use crate::vm::Collection;
468            // Do not block for GC if the `tls` does not represent a valid mutator thread. This
469            // allows non-mutator threads to trigger GC but not block for GC.
470            //
471            // FIXME: Make a proper API that allows `handle_user_collection_request` to be called by
472            // non-mutators and/or not trigger GC.
473            if !tls.0 .0.is_null() {
474                VM::VMCollection::block_for_gc(tls);
475            }
476            true
477        } else {
478            false
479        }
480    }
481
482    /// MMTK has requested stop-the-world activity (e.g., stw within a concurrent gc).
483    #[allow(unused)]
484    pub fn trigger_internal_collection_request(&self) {
485        self.gc_trigger.trigger_internal_collection_request();
486    }
487
488    /// Get a reference to the plan.
489    pub fn get_plan(&self) -> &dyn Plan<VM = VM> {
490        unsafe { &**(self.plan.get()) }
491    }
492
493    /// Get the plan as mutable reference.
494    ///
495    /// # Safety
496    ///
497    /// This is unsafe because the caller must ensure that the plan is not used by other threads.
498    #[allow(clippy::mut_from_ref)]
499    pub unsafe fn get_plan_mut(&self) -> &mut dyn Plan<VM = VM> {
500        &mut **(self.plan.get())
501    }
502
503    /// Get the run time options.
504    pub fn get_options(&self) -> &Options {
505        &self.options
506    }
507
508    /// Enumerate objects in all spaces in this MMTK instance.
509    ///
510    /// The call-back function `f` is called for every object that has the valid object bit (VO
511    /// bit), i.e. objects that are allocated in the heap of this MMTK instance, but has not been
512    /// reclaimed, yet.
513    ///
514    /// # Notes about object initialization and finalization
515    ///
516    /// When this function visits an object, it only guarantees that its VO bit must have been set.
517    /// It is not guaranteed if the object has been "fully initialized" in the sense of the
518    /// programming language the VM is implementing.  For example, the object header and the type
519    /// information may not have been written.
520    ///
521    /// It will also visit objects that have been "finalized" in the sense of the programming
522    /// langauge the VM is implementing, as long as the object has not been reclaimed by the GC,
523    /// yet.  Be careful.  If the object header is destroyed, it may not be safe to access such
524    /// objects in the high-level language.
525    ///
526    /// # Interaction with allocation and GC
527    ///
528    /// This function does not mutate the heap.  It is safe if multiple threads execute this
529    /// function concurrently during mutator time.
530    ///
531    /// It has *undefined behavior* if allocation or GC happens while this function is being
532    /// executed.  The VM binding must ensure no threads are allocating and GC does not start while
533    /// executing this function.  One way to do this is stopping all mutators before calling this
534    /// function.
535    ///
536    /// Some high-level languages may provide an API that allows the user to allocate objects and
537    /// trigger GC while enumerating objects.  One example is [`ObjectSpace::each_object`][os_eo] in
538    /// Ruby.  The VM binding may use the callback of this function to save all visited object
539    /// references and let the user visit those references after this function returns.  Make sure
540    /// those saved references are in the root set or in an object that will live through GCs before
541    /// the high-level language finishes visiting the saved object references.
542    ///
543    /// [os_eo]: https://docs.ruby-lang.org/en/master/ObjectSpace.html#method-c-each_object
544    #[cfg(feature = "vo_bit")]
545    pub fn enumerate_objects<F>(&self, f: F)
546    where
547        F: FnMut(ObjectReference),
548    {
549        use crate::util::object_enum;
550
551        let mut enumerator = object_enum::ClosureObjectEnumerator::<_, VM>::new(f);
552        let plan = self.get_plan();
553        plan.for_each_space(&mut |space| {
554            space.enumerate_objects(&mut enumerator);
555        })
556    }
557
558    /// Aggregate a hash map of live bytes per space with the space stats to produce
559    /// a map of live bytes stats for the spaces.
560    pub(crate) fn aggregate_live_bytes_in_last_gc(
561        &self,
562        live_bytes_per_space: [usize; MAX_SPACES],
563    ) -> HashMap<&'static str, crate::LiveBytesStats> {
564        use crate::policy::space::Space;
565        let mut ret = HashMap::new();
566        self.get_plan().for_each_space(&mut |space: &dyn Space<VM>| {
567            let space_name = space.get_name();
568            let space_idx = space.get_descriptor().get_index();
569            let used_pages = space.reserved_pages();
570            if used_pages != 0 {
571                let used_bytes = crate::util::conversions::pages_to_bytes(used_pages);
572                let live_bytes = live_bytes_per_space[space_idx];
573                debug_assert!(
574                    live_bytes <= used_bytes,
575                    "Live bytes of objects in {} ({} bytes) is larger than used pages ({} bytes), something is wrong.",
576                    space_name, live_bytes, used_bytes
577                );
578                ret.insert(space_name, crate::LiveBytesStats {
579                    live_bytes,
580                    used_pages,
581                    used_bytes,
582                });
583            }
584        });
585        ret
586    }
587
588    /// Print VM maps.  It will print the memory ranges used by spaces as well as some attributes of
589    /// the spaces.
590    ///
591    /// -   "I": The space is immortal.  Its objects will never die.
592    /// -   "N": The space is non-movable.  Its objects will never move.
593    ///
594    /// Arguments:
595    /// *   `out`: the place to print the VM maps.
596    /// *   `space_name`: If `None`, print all spaces;
597    ///     if `Some(n)`, only print the space whose name is `n`.
598    pub fn debug_print_vm_maps(
599        &self,
600        out: &mut impl std::fmt::Write,
601        space_name: Option<&str>,
602    ) -> Result<(), std::fmt::Error> {
603        let mut result_so_far = Ok(());
604        self.get_plan().for_each_space(&mut |space| {
605            if result_so_far.is_ok()
606                && (space_name.is_none() || space_name == Some(space.get_name()))
607            {
608                result_so_far = crate::policy::space::print_vm_map(space, out);
609            }
610        });
611        result_so_far
612    }
613
614    /// Initialize object metadata for a VM space object.
615    /// Objects in the VM space are allocated/managed by the binding. This function provides a way for
616    /// the binding to set object metadata in MMTk for an object in the space.
617    #[cfg(feature = "vm_space")]
618    pub fn initialize_vm_space_object(&self, object: crate::util::ObjectReference) {
619        use crate::policy::sft::SFT;
620        let bytes = VM::VMObjectModel::get_current_size(object);
621        self.get_plan()
622            .base()
623            .vm_space
624            .initialize_object_metadata(object, bytes)
625    }
626}
627
628/// A non-mangled function to print object information for debugging purposes. This function can be directly
629/// called from a debugger.
630#[no_mangle]
631pub fn mmtk_debug_print_object(object: crate::util::ObjectReference) {
632    // If the address is unmapped, we cannot access its metadata. Just quit.
633    if !object.to_raw_address().is_mapped() {
634        println!("{} is not mapped in MMTk", object);
635        return;
636    }
637
638    // If the address is not aligned to the object reference size, it is not an object reference.
639    if !object
640        .to_raw_address()
641        .is_aligned_to(crate::util::ObjectReference::ALIGNMENT)
642    {
643        println!(
644            "{} is not properly aligned. It is not an object reference.",
645            object
646        );
647    }
648
649    // Forward to the space
650    let sft = SFT_MAP.get_checked(object.to_raw_address());
651    // Print the space name
652    println!("In {}:", sft.name());
653    // Print object information
654    sft.debug_print_object_info(object);
655}