Skip to content

Commit 7ff4562

Browse files
committed
Allow dump heap as JSON
1 parent 85ab012 commit 7ff4562

File tree

3 files changed

+111
-4
lines changed

3 files changed

+111
-4
lines changed

mmtk/Cargo.lock

Lines changed: 9 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mmtk/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ lazy_static = "1.1"
2727
# - change branch
2828
# - change repo name
2929
# But other changes including adding/removing whitespaces in commented lines may break the CI
30-
mmtk = { git = "https://github.com/mmtk/mmtk-core.git", rev = "4315d998f514735f4fcfd3d14ef4ada50e9e0e0d" }
30+
mmtk = { git = "https://github.com/mmtk/mmtk-core.git", rev = "ceea8cf9e4aca52220c674b8a95b4bc5ae0adcae" }
3131
# Uncomment the following to build locally
3232
# mmtk = { path = "../../mmtk-core" }
3333
log = {version = "0.4", features = ["max_level_trace", "release_max_level_off"] }
@@ -36,6 +36,7 @@ atomic = "0.4.6"
3636
chrono = "*"
3737
thread-id = "*"
3838
memoffset = "*"
39+
json = "0.12.4"
3940

4041
# ykstackmaps = { git = "https://github.com/udesou/ykstackmaps.git", branch = "udesou-master", version = "*" }
4142

@@ -65,3 +66,4 @@ immix_max_moving = ["mmtk/immix_stress_defrag", "mmtk/immix_defrag_every_block"]
6566
dump_memory_stats = ["mmtk/dump_memory_stats"]
6667

6768
address_based_hashing = []
69+
heap_dump = []

mmtk/src/collection.rs

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ use crate::{BLOCK_FOR_GC, STW_COND, WORLD_HAS_STOPPED};
1717
pub static GC_START: AtomicU64 = AtomicU64::new(0);
1818
static CURRENT_GC_MAY_MOVE: AtomicBool = AtomicBool::new(true);
1919

20+
// The current GC count. Used to track the number of GCs that have occurred, and what GC it is right now.
21+
// This count is bumped after a GC.
22+
static GC_COUNT: AtomicU64 = AtomicU64::new(0);
23+
2024
use std::collections::HashSet;
2125
use std::sync::RwLock;
2226
use std::thread::ThreadId;
@@ -54,6 +58,8 @@ impl Collection<JuliaVM> for VMCollection {
5458
}
5559

5660
trace!("Stopped the world!");
61+
#[cfg(feature = "heap_dump")]
62+
dump_heap(GC_COUNT.load(Ordering::SeqCst), 0);
5763

5864
// Store if the current GC may move objects -- we will use it when the current GC finishes.
5965
// We cache the value here just in case MMTk may clear it before we use the value.
@@ -93,7 +99,10 @@ impl Collection<JuliaVM> for VMCollection {
9399
)
94100
}
95101

102+
#[cfg(feature = "heap_dump")]
103+
dump_heap(GC_COUNT.load(Ordering::SeqCst), 1);
96104
// dump_immix_block_stats();
105+
GC_COUNT.fetch_add(1, Ordering::SeqCst);
97106

98107
AtomicBool::store(&BLOCK_FOR_GC, false, Ordering::SeqCst);
99108
AtomicBool::store(&WORLD_HAS_STOPPED, false, Ordering::SeqCst);
@@ -244,7 +253,96 @@ pub fn dump_immix_block_stats() {
244253
}
245254
},
246255
);
256+
print_fragmentation();
247257
}
258+
}
248259

249-
print_fragmentation();
260+
#[cfg(feature = "heap_dump")]
261+
pub fn dump_heap(gc_count: u64, phase: u64) { // phase: 0 for pre-gc, 1 for post-gc
262+
println!("Dumping heap for GC {} phase {}", gc_count, phase);
263+
use mmtk::util::heap::inspection::*;
264+
use mmtk::vm::ObjectModel;
265+
use mmtk::util::constants::LOG_BYTES_IN_PAGE;
266+
use json::JsonValue;
267+
use std::fs::File;
268+
use std::io::Write;
269+
270+
fn space_into_json(space: &dyn SpaceInspector) -> JsonValue {
271+
let mut json = JsonValue::new_object();
272+
json["name"] = JsonValue::String(space.space_name().to_string());
273+
json["policy"] = JsonValue::String(space.policy_name().to_string());
274+
json["used_bytes"] = JsonValue::Number((space.used_pages() << LOG_BYTES_IN_PAGE).into());
275+
json
276+
}
277+
278+
fn region_into_json(space: &dyn SpaceInspector, region: &dyn RegionInspector) -> JsonValue{
279+
// println!("Dumping region: {} {}", region.region_type(), region.start());
280+
let mut json = JsonValue::new_object();
281+
json["type"] = JsonValue::String(region.region_type().to_string());
282+
json["start"] = JsonValue::String(format!("{}", region.start()));
283+
json["size"] = JsonValue::Number(region.size().into());
284+
285+
let sub_regions = space.list_sub_regions(region);
286+
if sub_regions.is_empty() {
287+
let objects = region.list_objects();
288+
json["objects"] = objects.into_iter().map(|object| {
289+
let mut obj_json = JsonValue::new_object();
290+
obj_json["address"] = JsonValue::String(format!("{}", object));
291+
obj_json["type"] = JsonValue::String(unsafe {
292+
crate::julia_scanning::get_julia_object_type(object.to_raw_address())
293+
});
294+
obj_json["size"] = JsonValue::Number(
295+
unsafe { crate::object_model::VMObjectModel::get_current_size(object) }.into(),
296+
);
297+
obj_json
298+
}).collect::<Vec<_>>().into();
299+
} else {
300+
json["regions"] = sub_regions.into_iter().map(|sub_region| {
301+
region_into_json(space, &*sub_region)
302+
}).collect::<Vec<_>>().into();
303+
}
304+
305+
json
306+
}
307+
308+
// Dump high-levvel space information
309+
{
310+
let mut file = File::create(format!(
311+
"spaces_gc_{}_phase_{}.json",
312+
gc_count,
313+
phase
314+
)).unwrap();
315+
let mut json = JsonValue::new_array();
316+
SINGLETON.inspect_spaces().iter().for_each(|space| json.push(space_into_json(*space)).unwrap());
317+
file.write_all(json::stringify_pretty(json, 2).as_bytes()).unwrap();
318+
}
319+
320+
// Dump Immix heap -- be careful we only dump one chunk at a time to avoid using too much Rust memory and get OOM
321+
{
322+
let mut file = File::create(format!(
323+
"immix_heap_gc_{}_phase_{}.json",
324+
gc_count,
325+
phase
326+
)).unwrap();
327+
328+
file.write_all(b"[\n").unwrap();
329+
330+
let immix_space = SINGLETON.inspect_spaces().into_iter()
331+
.find(|s| s.space_name() == "immix")
332+
.expect("Immix space not found");
333+
let chunks = immix_space.list_top_regions();
334+
let n_chunks = chunks.len();
335+
let mut i = 0;
336+
chunks.into_iter().for_each(|chunk: Box<dyn RegionInspector>| {
337+
let json = region_into_json(immix_space, &*chunk);
338+
file.write_all(json::stringify_pretty(json, 2).as_bytes()).unwrap();
339+
if i != n_chunks - 1 {
340+
file.write_all(b",\n").unwrap();
341+
}
342+
i += 1;
343+
});
344+
assert!(i == n_chunks);
345+
346+
file.write_all(b"]\n").unwrap();
347+
}
250348
}

0 commit comments

Comments
 (0)