@@ -17,6 +17,10 @@ use crate::{BLOCK_FOR_GC, STW_COND, WORLD_HAS_STOPPED};
17
17
pub static GC_START : AtomicU64 = AtomicU64 :: new ( 0 ) ;
18
18
static CURRENT_GC_MAY_MOVE : AtomicBool = AtomicBool :: new ( true ) ;
19
19
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
+
20
24
use std:: collections:: HashSet ;
21
25
use std:: sync:: RwLock ;
22
26
use std:: thread:: ThreadId ;
@@ -54,6 +58,8 @@ impl Collection<JuliaVM> for VMCollection {
54
58
}
55
59
56
60
trace ! ( "Stopped the world!" ) ;
61
+ #[ cfg( feature = "heap_dump" ) ]
62
+ dump_heap ( GC_COUNT . load ( Ordering :: SeqCst ) , 0 ) ;
57
63
58
64
// Store if the current GC may move objects -- we will use it when the current GC finishes.
59
65
// 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 {
93
99
)
94
100
}
95
101
102
+ #[ cfg( feature = "heap_dump" ) ]
103
+ dump_heap ( GC_COUNT . load ( Ordering :: SeqCst ) , 1 ) ;
96
104
// dump_immix_block_stats();
105
+ GC_COUNT . fetch_add ( 1 , Ordering :: SeqCst ) ;
97
106
98
107
AtomicBool :: store ( & BLOCK_FOR_GC , false , Ordering :: SeqCst ) ;
99
108
AtomicBool :: store ( & WORLD_HAS_STOPPED , false , Ordering :: SeqCst ) ;
@@ -244,7 +253,96 @@ pub fn dump_immix_block_stats() {
244
253
}
245
254
} ,
246
255
) ;
256
+ print_fragmentation ( ) ;
247
257
}
258
+ }
248
259
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
+ }
250
348
}
0 commit comments