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