Skip to content

Commit 06284e1

Browse files
committed
Adding feature to dump memory stats (from los and immixspace)
1 parent 1f39198 commit 06284e1

File tree

7 files changed

+95
-82
lines changed

7 files changed

+95
-82
lines changed

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ malloc_counted_size = []
153153
# Count the size of all live objects in GC
154154
count_live_bytes_in_gc = []
155155

156-
# Count the size of live objects in immixspace (the ones that can actually be opportunistically moved)
157-
count_live_bytes_immixspace = []
156+
# Dump memory stats about the plan (live bytes, live lines, live blocks, and used pages)
157+
dump_memory_stats = []
158158

159159
# Workaround a problem where bpftrace scripts (see tools/tracing/timeline/capture.bt) cannot
160160
# capture the type names of work packets.

src/memory_manager.rs

-24
Original file line numberDiff line numberDiff line change
@@ -557,30 +557,6 @@ pub fn live_bytes_in_last_gc<VM: VMBinding>(mmtk: &MMTK<VM>) -> usize {
557557
mmtk.state.live_bytes_in_last_gc.load(Ordering::SeqCst)
558558
}
559559

560-
/// Return the percentage of occupation of the immixspace (e.g. 42.98 percent). To do that we count the size of every live object
561-
/// in the immixspace. Since MMTk accounts for memory in pages, we return the ratio between this number and
562-
/// the number of used bytes (according to the used pages by the immixspace).
563-
/// The value returned by this method is only updated when we finish tracing in a GC. A recommended timing
564-
/// to call this method is at the end of a GC (e.g. when the runtime is about to resume threads).
565-
#[cfg(feature = "count_live_bytes_immixspace")]
566-
pub fn occupation_rate_in_immixspace<VM: VMBinding>(mmtk: &MMTK<VM>) -> f64 {
567-
use crate::policy::immix::ImmixSpace;
568-
use crate::policy::space::Space;
569-
let mut rate = None;
570-
mmtk.get_plan()
571-
.for_each_space(&mut |space: &dyn Space<VM>| {
572-
if let Some(immix) = space.downcast_ref::<ImmixSpace<VM>>() {
573-
assert!(
574-
rate.is_none(),
575-
"There are multiple Immix spaces in the plan."
576-
);
577-
// Get the stats here from ImmixSpace
578-
rate = Some(immix.get_occupation_rate());
579-
}
580-
});
581-
rate.expect("No Immix space in the plan.") as f64 / 100.0
582-
}
583-
584560
/// Return the starting address of the heap. *Note that currently MMTk uses
585561
/// a fixed address range as heap.*
586562
pub fn starting_heap_address() -> Address {

src/plan/global.rs

+4
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ pub trait Plan: 'static + HasSpaces + Sync + Downcast {
315315
space.verify_side_metadata_sanity(&mut side_metadata_sanity_checker);
316316
})
317317
}
318+
319+
// Dump memory stats for the plan
320+
#[cfg(feature = "dump_memory_stats")]
321+
fn dump_memory_stats(&self) {}
318322
}
319323

320324
impl_downcast!(Plan assoc VM);

src/plan/immix/global.rs

+6
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ impl<VM: VMBinding> Plan for Immix<VM> {
116116
fn common(&self) -> &CommonPlan<VM> {
117117
&self.common
118118
}
119+
120+
#[cfg(feature = "dump_memory_stats")]
121+
fn dump_memory_stats(&self) {
122+
self.immix_space.dump_memory_stats();
123+
self.common.los.dump_memory_stats();
124+
}
119125
}
120126

121127
impl<VM: VMBinding> Immix<VM> {

src/policy/immix/immixspace.rs

+35-56
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,9 @@ pub struct ImmixSpace<VM: VMBinding> {
5454
scheduler: Arc<GCWorkScheduler<VM>>,
5555
/// Some settings for this space
5656
space_args: ImmixSpaceArgs,
57-
/// Keeping track of fragmentation rate
58-
#[cfg(feature = "count_live_bytes_immixspace")]
59-
live_bytes_in_immixspace: AtomicUsize,
60-
#[cfg(feature = "count_live_bytes_immixspace")]
61-
occupation_rate: AtomicUsize,
57+
/// Keeping track of live bytes
58+
#[cfg(feature = "dump_memory_stats")]
59+
live_bytes: AtomicUsize,
6260
}
6361

6462
/// Some arguments for Immix Space.
@@ -223,9 +221,8 @@ impl<VM: VMBinding> crate::policy::gc_work::PolicyTraceObject<VM> for ImmixSpace
223221
self.mark_lines(object);
224222
}
225223

226-
// count the bytes for each object in immixspace to
227-
// check for fragmentation
228-
#[cfg(feature = "count_live_bytes_immixspace")]
224+
// count the bytes for each object in immixspace
225+
#[cfg(feature = "dump_memory_stats")]
229226
self.increase_live_bytes(VM::VMObjectModel::get_current_size(object));
230227
}
231228

@@ -325,10 +322,8 @@ impl<VM: VMBinding> ImmixSpace<VM> {
325322
mark_state: Self::MARKED_STATE,
326323
scheduler: scheduler.clone(),
327324
space_args,
328-
#[cfg(feature = "count_live_bytes_immixspace")]
329-
live_bytes_in_immixspace: AtomicUsize::new(0),
330-
#[cfg(feature = "count_live_bytes_immixspace")]
331-
occupation_rate: AtomicUsize::new(0),
325+
#[cfg(feature = "dump_memory_stats")]
326+
live_bytes: AtomicUsize::new(0),
332327
}
333328
}
334329

@@ -451,7 +446,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
451446
}
452447
}
453448

454-
#[cfg(feature = "count_live_bytes_immixspace")]
449+
#[cfg(feature = "dump_memory_stats")]
455450
self.set_live_bytes(0);
456451
}
457452

@@ -481,14 +476,11 @@ impl<VM: VMBinding> ImmixSpace<VM> {
481476

482477
self.lines_consumed.store(0, Ordering::Relaxed);
483478

484-
// calculate the fragmentation rate
485-
#[cfg(feature = "count_live_bytes_immixspace")]
486-
self.dump_memory_stats();
487-
488479
did_defrag
489480
}
490481

491-
fn dump_memory_stats(&mut self) {
482+
#[cfg(feature = "dump_memory_stats")]
483+
pub(crate) fn dump_memory_stats(&self) {
492484
#[derive(Default)]
493485
struct Dist {
494486
live_blocks: usize,
@@ -505,11 +497,18 @@ impl<VM: VMBinding> ImmixSpace<VM> {
505497
.filter(|b| b.get_state() != BlockState::Unallocated)
506498
{
507499
dist.live_blocks += 1;
508-
for _line in block
509-
.lines()
510-
.filter(|l| l.is_marked(self.line_mark_state.load(Ordering::Acquire)))
511-
{
512-
dist.live_lines = 1;
500+
match block.get_state() {
501+
BlockState::Marked => {
502+
panic!("At this point the block should have been swept already");
503+
}
504+
BlockState::Unmarked => {
505+
// Block is unmarked and cannot be reused (has no holes)
506+
dist.live_lines += Block::LINES;
507+
}
508+
BlockState::Reusable { unavailable_lines } => {
509+
dist.live_lines += unavailable_lines as usize;
510+
}
511+
BlockState::Unallocated => {}
513512
}
514513
}
515514
}
@@ -518,31 +517,22 @@ impl<VM: VMBinding> ImmixSpace<VM> {
518517
"{} immixspace",
519518
chrono::offset::Local::now().format("%Y-%m-%d %H:%M:%S")
520519
);
521-
println!("Live bytes = {}", self.get_live_bytes());
522-
println!("Reserved pages = {}", self.reserved_pages());
520+
println!("\tLive bytes = {}", self.get_live_bytes());
521+
println!("\tReserved pages = {}", self.reserved_pages());
523522
println!(
524-
"Reserved pages (bytes) = {}",
523+
"\tReserved pages (bytes) = {}",
525524
self.reserved_pages() << LOG_BYTES_IN_PAGE
526525
);
527-
println!("Live blocks = {}", dist.live_blocks);
526+
println!("\tLive blocks = {}", dist.live_blocks);
528527
println!(
529-
"Live blocks (bytes) = {}",
528+
"\tLive blocks (bytes) = {}",
530529
dist.live_blocks << Block::LOG_BYTES
531530
);
532-
println!("Live lines = {}", dist.live_lines);
531+
println!("\tLive lines = {}", dist.live_lines);
533532
println!(
534-
"Live lines (bytes) = {}",
533+
"\tLive lines (bytes) = {}",
535534
dist.live_lines << Line::LOG_BYTES
536535
);
537-
538-
let o_rate: f64 =
539-
self.get_live_bytes() as f64 / (self.reserved_pages() << LOG_BYTES_IN_PAGE) as f64;
540-
541-
let o_rate_usize: usize = (o_rate * 10000.0) as usize;
542-
543-
debug_assert!((0.0..=1.0).contains(&o_rate));
544-
545-
self.set_occupation_rate(o_rate_usize);
546536
}
547537

548538
/// Generate chunk sweep tasks
@@ -886,30 +876,19 @@ impl<VM: VMBinding> ImmixSpace<VM> {
886876
}
887877
}
888878

889-
#[cfg(feature = "count_live_bytes_immixspace")]
879+
#[cfg(feature = "dump_memory_stats")]
890880
pub fn get_live_bytes(&self) -> usize {
891-
self.live_bytes_in_immixspace.load(Ordering::SeqCst)
881+
self.live_bytes.load(Ordering::SeqCst)
892882
}
893883

894-
#[cfg(feature = "count_live_bytes_immixspace")]
884+
#[cfg(feature = "dump_memory_stats")]
895885
pub fn set_live_bytes(&self, size: usize) {
896-
self.live_bytes_in_immixspace.store(size, Ordering::SeqCst)
886+
self.live_bytes.store(size, Ordering::SeqCst)
897887
}
898888

899-
#[cfg(feature = "count_live_bytes_immixspace")]
889+
#[cfg(feature = "dump_memory_stats")]
900890
pub fn increase_live_bytes(&self, size: usize) {
901-
self.live_bytes_in_immixspace
902-
.fetch_add(size, Ordering::SeqCst);
903-
}
904-
905-
#[cfg(feature = "count_live_bytes_immixspace")]
906-
pub fn get_occupation_rate(&self) -> usize {
907-
self.occupation_rate.load(Ordering::SeqCst)
908-
}
909-
910-
#[cfg(feature = "count_live_bytes_immixspace")]
911-
pub fn set_occupation_rate(&self, size: usize) {
912-
self.occupation_rate.store(size, Ordering::SeqCst);
891+
self.live_bytes.fetch_add(size, Ordering::SeqCst);
913892
}
914893
}
915894

src/policy/largeobjectspace.rs

+45
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ use crate::policy::sft::GCWorkerMutRef;
66
use crate::policy::sft::SFT;
77
use crate::policy::space::{CommonSpace, Space};
88
use crate::util::constants::BYTES_IN_PAGE;
9+
#[cfg(feature = "dump_memory_stats")]
10+
use crate::util::constants::LOG_BYTES_IN_PAGE;
911
use crate::util::heap::{FreeListPageResource, PageResource};
1012
use crate::util::metadata;
1113
use crate::util::opaque_pointer::*;
1214
use crate::util::treadmill::TreadMill;
1315
use crate::util::{Address, ObjectReference};
1416
use crate::vm::ObjectModel;
1517
use crate::vm::VMBinding;
18+
#[cfg(feature = "dump_memory_stats")]
19+
use std::sync::atomic::AtomicUsize;
1620

1721
#[allow(unused)]
1822
const PAGE_MASK: usize = !(BYTES_IN_PAGE - 1);
@@ -28,6 +32,9 @@ pub struct LargeObjectSpace<VM: VMBinding> {
2832
mark_state: u8,
2933
in_nursery_gc: bool,
3034
treadmill: TreadMill,
35+
/// Keeping track of live bytes
36+
#[cfg(feature = "dump_memory_stats")]
37+
live_bytes: AtomicUsize,
3138
}
3239

3340
impl<VM: VMBinding> SFT for LargeObjectSpace<VM> {
@@ -136,6 +143,11 @@ impl<VM: VMBinding> crate::policy::gc_work::PolicyTraceObject<VM> for LargeObjec
136143
fn may_move_objects<const KIND: crate::policy::gc_work::TraceKind>() -> bool {
137144
false
138145
}
146+
147+
#[cfg(feature = "dump_memory_stats")]
148+
fn post_scan_object(&self, object: ObjectReference) {
149+
self.increase_live_bytes(VM::VMObjectModel::get_current_size(object));
150+
}
139151
}
140152

141153
impl<VM: VMBinding> LargeObjectSpace<VM> {
@@ -162,6 +174,8 @@ impl<VM: VMBinding> LargeObjectSpace<VM> {
162174
mark_state: 0,
163175
in_nursery_gc: false,
164176
treadmill: TreadMill::new(),
177+
#[cfg(feature = "dump_memory_stats")]
178+
live_bytes: AtomicUsize::new(0),
165179
}
166180
}
167181

@@ -172,6 +186,8 @@ impl<VM: VMBinding> LargeObjectSpace<VM> {
172186
}
173187
self.treadmill.flip(full_heap);
174188
self.in_nursery_gc = !full_heap;
189+
#[cfg(feature = "dump_memory_stats")]
190+
self.set_live_bytes(0);
175191
}
176192

177193
pub fn release(&mut self, full_heap: bool) {
@@ -303,6 +319,35 @@ impl<VM: VMBinding> LargeObjectSpace<VM> {
303319
) & NURSERY_BIT
304320
== NURSERY_BIT
305321
}
322+
323+
#[cfg(feature = "dump_memory_stats")]
324+
pub fn get_live_bytes(&self) -> usize {
325+
self.live_bytes.load(Ordering::SeqCst)
326+
}
327+
328+
#[cfg(feature = "dump_memory_stats")]
329+
pub fn set_live_bytes(&self, size: usize) {
330+
self.live_bytes.store(size, Ordering::SeqCst)
331+
}
332+
333+
#[cfg(feature = "dump_memory_stats")]
334+
pub fn increase_live_bytes(&self, size: usize) {
335+
self.live_bytes.fetch_add(size, Ordering::SeqCst);
336+
}
337+
338+
#[cfg(feature = "dump_memory_stats")]
339+
pub(crate) fn dump_memory_stats(&self) {
340+
println!(
341+
"{} los",
342+
chrono::offset::Local::now().format("%Y-%m-%d %H:%M:%S")
343+
);
344+
println!("\tLive bytes = {}", self.get_live_bytes());
345+
println!("\tReserved pages = {}", self.reserved_pages());
346+
println!(
347+
"\tReserved pages (bytes) = {}",
348+
self.reserved_pages() << LOG_BYTES_IN_PAGE
349+
);
350+
}
306351
}
307352

308353
fn get_super_page(cell: Address) -> Address {

src/scheduler/gc_work.rs

+3
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ impl<VM: VMBinding> GCWork<VM> for EndOfGC {
244244
);
245245
}
246246

247+
#[cfg(feature = "dump_memory_stats")]
248+
mmtk.get_plan().dump_memory_stats();
249+
247250
// We assume this is the only running work packet that accesses plan at the point of execution
248251
let plan_mut: &mut dyn Plan<VM = VM> = unsafe { mmtk.get_plan_mut() };
249252
plan_mut.end_of_gc(worker.tls);

0 commit comments

Comments
 (0)