From 668de29b001c1c557c6412ef8c0dd68d90f75f41 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 17 Oct 2024 09:58:01 +1300 Subject: [PATCH 1/8] Update CI macos image to macos-15 --- .github/workflows/minimal-tests-core.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/minimal-tests-core.yml b/.github/workflows/minimal-tests-core.yml index 3a72c531e7..7334488d63 100644 --- a/.github/workflows/minimal-tests-core.yml +++ b/.github/workflows/minimal-tests-core.yml @@ -36,7 +36,7 @@ jobs: target: - { os: ubuntu-22.04, triple: x86_64-unknown-linux-gnu } - { os: ubuntu-22.04, triple: i686-unknown-linux-gnu } - - { os: macos-12, triple: x86_64-apple-darwin } + - { os: macos-15, triple: x86_64-apple-darwin } rust: ${{ fromJson(needs.setup-test-matrix.outputs.rust )}} name: minimal-tests-core/${{ matrix.target.triple }}/${{ matrix.rust }} From 25a96df59d582097671ed317b3471af766c11692 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 17 Oct 2024 12:52:54 +1300 Subject: [PATCH 2/8] Get process memory map on macos --- src/util/memory.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/util/memory.rs b/src/util/memory.rs index bd550c599a..12f299600a 100644 --- a/src/util/memory.rs +++ b/src/util/memory.rs @@ -299,9 +299,34 @@ pub fn get_process_memory_maps() -> String { data } +#[cfg(target_os = "macos")] +pub fn get_process_memory_maps() -> String { + // Get the current process ID (replace this with a specific PID if needed) + let pid = std::process::id(); + + // Execute the vmmap command + let output = std::process::Command::new("vmmap") + .arg(pid.to_string()) // Pass the PID as an argument + .output() // Capture the output + .expect("Failed to execute vmmap command"); + + // Check if the command was successful + if output.status.success() { + // Convert the command output to a string + let output_str = std::str::from_utf8(&output.stdout) + .expect("Failed to convert output to string"); + output_str.to_string() + } else { + // Handle the error case + let error_message = std::str::from_utf8(&output.stderr) + .expect("Failed to convert error message to string"); + panic!("Failed to get process memory map: {}", error_message) + } +} + /// Get the memory maps for the process. The returned string is a multi-line string. /// This is only meant to be used for debugging. For example, log process memory maps after detecting a clash. -#[cfg(not(any(target_os = "linux", target_os = "android")))] +#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "macos")))] pub fn get_process_memory_maps() -> String { "(process map unavailable)".to_string() } From 859211d5d35776bea02c726c9e73053e04da410f Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 17 Oct 2024 13:20:59 +1300 Subject: [PATCH 3/8] Print the address that we failed to mmap --- src/policy/space.rs | 2 +- src/util/memory.rs | 26 +++++++++++-------- .../mock_test_handle_mmap_conflict.rs | 2 ++ .../mock_tests/mock_test_handle_mmap_oom.rs | 2 ++ 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/policy/space.rs b/src/policy/space.rs index 053b636963..8048009b20 100644 --- a/src/policy/space.rs +++ b/src/policy/space.rs @@ -151,7 +151,7 @@ pub trait Space: 'static + SFT + Sync + Downcast { .try_map_metadata_space(res.start, bytes), ) { - memory::handle_mmap_error::(mmap_error, tls); + memory::handle_mmap_error::(mmap_error, tls, res.start, bytes); } }; let grow_space = || { diff --git a/src/util/memory.rs b/src/util/memory.rs index 12f299600a..4f5abe01de 100644 --- a/src/util/memory.rs +++ b/src/util/memory.rs @@ -187,13 +187,20 @@ pub fn munmap(start: Address, size: usize) -> Result<()> { /// Properly handle errors from a mmap Result, including invoking the binding code in the case of /// an OOM error. -pub fn handle_mmap_error(error: Error, tls: VMThread) -> ! { +pub fn handle_mmap_error( + error: Error, + tls: VMThread, + addr: Address, + bytes: usize, +) -> ! { use std::io::ErrorKind; + eprintln!("Failed to mmap {} - {}", addr, addr + bytes); + eprintln!("{}", get_process_memory_maps()); + match error.kind() { // From Rust nightly 2021-05-12, we started to see Rust added this ErrorKind. ErrorKind::OutOfMemory => { - eprintln!("{}", get_process_memory_maps()); // Signal `MmapOutOfMemory`. Expect the VM to abort immediately. trace!("Signal MmapOutOfMemory!"); VM::VMCollection::out_of_memory(tls, AllocationError::MmapOutOfMemory); @@ -206,7 +213,6 @@ pub fn handle_mmap_error(error: Error, tls: VMThread) -> ! { if let Some(os_errno) = error.raw_os_error() { // If it is OOM, we invoke out_of_memory() through the VM interface. if os_errno == libc::ENOMEM { - eprintln!("{}", get_process_memory_maps()); // Signal `MmapOutOfMemory`. Expect the VM to abort immediately. trace!("Signal MmapOutOfMemory!"); VM::VMCollection::out_of_memory(tls, AllocationError::MmapOutOfMemory); @@ -215,12 +221,10 @@ pub fn handle_mmap_error(error: Error, tls: VMThread) -> ! { } } ErrorKind::AlreadyExists => { - eprintln!("{}", get_process_memory_maps()); panic!("Failed to mmap, the address is already mapped. Should MMTk quarantine the address range first?"); } _ => {} } - eprintln!("{}", get_process_memory_maps()); panic!("Unexpected mmap failure: {:?}", error) } @@ -306,20 +310,20 @@ pub fn get_process_memory_maps() -> String { // Execute the vmmap command let output = std::process::Command::new("vmmap") - .arg(pid.to_string()) // Pass the PID as an argument - .output() // Capture the output + .arg(pid.to_string()) // Pass the PID as an argument + .output() // Capture the output .expect("Failed to execute vmmap command"); // Check if the command was successful if output.status.success() { // Convert the command output to a string - let output_str = std::str::from_utf8(&output.stdout) - .expect("Failed to convert output to string"); + let output_str = + std::str::from_utf8(&output.stdout).expect("Failed to convert output to string"); output_str.to_string() } else { // Handle the error case - let error_message = std::str::from_utf8(&output.stderr) - .expect("Failed to convert error message to string"); + let error_message = + std::str::from_utf8(&output.stderr).expect("Failed to convert error message to string"); panic!("Failed to get process memory map: {}", error_message) } } diff --git a/src/vm/tests/mock_tests/mock_test_handle_mmap_conflict.rs b/src/vm/tests/mock_tests/mock_test_handle_mmap_conflict.rs index b36ccaef7d..b4cc7be194 100644 --- a/src/vm/tests/mock_tests/mock_test_handle_mmap_conflict.rs +++ b/src/vm/tests/mock_tests/mock_test_handle_mmap_conflict.rs @@ -22,6 +22,8 @@ pub fn test_handle_mmap_conflict() { memory::handle_mmap_error::( mmap2_res.err().unwrap(), VMThread::UNINITIALIZED, + start, + one_megabyte, ); }); diff --git a/src/vm/tests/mock_tests/mock_test_handle_mmap_oom.rs b/src/vm/tests/mock_tests/mock_test_handle_mmap_oom.rs index 357169c78c..3e23db0fcb 100644 --- a/src/vm/tests/mock_tests/mock_test_handle_mmap_oom.rs +++ b/src/vm/tests/mock_tests/mock_test_handle_mmap_oom.rs @@ -24,6 +24,8 @@ pub fn test_handle_mmap_oom() { memory::handle_mmap_error::( mmap_res.err().unwrap(), VMThread::UNINITIALIZED, + start, + LARGE_SIZE, ); }); assert!(panic_res.is_err()); From c24f92fa5212ce63ed5049ec5151345cefce472d Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 17 Oct 2024 13:36:54 +1300 Subject: [PATCH 4/8] Use a different heap start for test_vm_layout_compressed_pointer --- src/util/memory.rs | 2 +- .../mock_tests/mock_test_vm_layout_compressed_pointer.rs | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/util/memory.rs b/src/util/memory.rs index 4f5abe01de..08c4dd48a4 100644 --- a/src/util/memory.rs +++ b/src/util/memory.rs @@ -195,7 +195,7 @@ pub fn handle_mmap_error( ) -> ! { use std::io::ErrorKind; - eprintln!("Failed to mmap {} - {}", addr, addr + bytes); + eprintln!("Failed to mmap {}, size {}", addr, bytes); eprintln!("{}", get_process_memory_maps()); match error.kind() { diff --git a/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs b/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs index 64146591db..72cc127dae 100644 --- a/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs +++ b/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs @@ -13,12 +13,7 @@ fn test_vm_layout_compressed_pointer() { with_mockvm( default_setup, || { - let start = if cfg!(target_os = "macos") { - // Impossible to map 0x4000_0000 on maocOS. SO choose a different address. - 0x40_0000_0000 - } else { - 0x4000_0000 - }; + let start = 0x4000_0000; let heap_size = 1024 * 1024; let end = match start + heap_size { end if end <= (4usize << 30) => 4usize << 30, From dfe44e66fe3109bec3c182ab10865e4201e2153f Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 17 Oct 2024 14:00:26 +1300 Subject: [PATCH 5/8] Use a different heap start for test_vm_layout_compressed_pointer --- src/util/memory.rs | 2 ++ .../mock_tests/mock_test_vm_layout_compressed_pointer.rs | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/util/memory.rs b/src/util/memory.rs index 08c4dd48a4..771068aaff 100644 --- a/src/util/memory.rs +++ b/src/util/memory.rs @@ -303,6 +303,8 @@ pub fn get_process_memory_maps() -> String { data } +/// Get the memory maps for the process. The returned string is a multi-line string. +/// This is only meant to be used for debugging. For example, log process memory maps after detecting a clash. #[cfg(target_os = "macos")] pub fn get_process_memory_maps() -> String { // Get the current process ID (replace this with a specific PID if needed) diff --git a/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs b/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs index 72cc127dae..c98b5e93f6 100644 --- a/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs +++ b/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs @@ -13,7 +13,12 @@ fn test_vm_layout_compressed_pointer() { with_mockvm( default_setup, || { - let start = 0x4000_0000; + let start = if cfg!(target_os = "macos") { + // Impossible to map 0x4000_0000 on maocOS. SO choose a different address. + 0x60_0000_0000 + } else { + 0x4000_0000 + }; let heap_size = 1024 * 1024; let end = match start + heap_size { end if end <= (4usize << 30) => 4usize << 30, From d87613fd70885b9827aabb645758d493e14b304e Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 17 Oct 2024 15:40:44 +1300 Subject: [PATCH 6/8] Figure out what address range is available for testing --- .github/scripts/ci-test.sh | 2 +- src/util/memory.rs | 19 +++++++++++++++++++ .../mock_test_vm_layout_compressed_pointer.rs | 16 +++++++--------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/.github/scripts/ci-test.sh b/.github/scripts/ci-test.sh index c25a22ec7c..54182e9d4f 100755 --- a/.github/scripts/ci-test.sh +++ b/.github/scripts/ci-test.sh @@ -34,7 +34,7 @@ find ./src ./tests -type f -name "mock_test_*" | while read -r file; do # Run the test with each plan it needs. for MMTK_PLAN in $PLANS; do - env MMTK_PLAN=$MMTK_PLAN cargo test --features mock_test,"$FEATURES" -- $t; + env MMTK_PLAN=$MMTK_PLAN cargo test --features mock_test,"$FEATURES" -- $t --nocapture; done done diff --git a/src/util/memory.rs b/src/util/memory.rs index 771068aaff..7f9c2135ef 100644 --- a/src/util/memory.rs +++ b/src/util/memory.rs @@ -356,6 +356,25 @@ pub(crate) fn get_system_total_memory() -> u64 { sys.total_memory() } +/// Find the given bytes that can be mmapped. This will do an anonymous mapping, record the return +/// address and then unmap the mapping. This is only used for testing. +#[cfg(test)] +pub fn find_usable_address(bytes: usize, align: usize) -> Option
{ + let flags = libc::MAP_ANON | libc::MAP_PRIVATE; + let prot = MmapStrategy::TEST.prot.into_native_flags(); + // Map extra to make sure that the return address is aligned. + let size_to_map = bytes + align; + let ret = unsafe { libc::mmap(std::ptr::null_mut(), size_to_map, prot, flags, -1, 0) }; + if ret as isize == -1 { + None + } else { + let usable_addr = Address::from_mut_ptr(ret); + munmap(usable_addr, size_to_map).unwrap(); + let ret = usable_addr.align_up(align); + Some(ret) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs b/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs index c98b5e93f6..2a1bf87755 100644 --- a/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs +++ b/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs @@ -4,6 +4,7 @@ use super::mock_test_prelude::*; use super::mock_test_vm_layout_default::test_with_vm_layout; use crate::util::conversions::*; use crate::util::heap::vm_layout::VMLayout; +use crate::util::heap::vm_layout::BYTES_IN_CHUNK; use crate::util::Address; // This test only run on 64bits. @@ -13,21 +14,18 @@ fn test_vm_layout_compressed_pointer() { with_mockvm( default_setup, || { - let start = if cfg!(target_os = "macos") { - // Impossible to map 0x4000_0000 on maocOS. SO choose a different address. - 0x60_0000_0000 - } else { - 0x4000_0000 - }; let heap_size = 1024 * 1024; - let end = match start + heap_size { + let start = crate::util::memory::find_usable_address(heap_size, BYTES_IN_CHUNK) + .expect("Cannot find usable address range"); + println!("Use {} as heap start", start); + let end = match start.as_usize() + heap_size { end if end <= (4usize << 30) => 4usize << 30, end if end <= (32usize << 30) => 32usize << 30, - _ => start + (32usize << 30), + _ => start.as_usize() + (32usize << 30), }; let layout = VMLayout { log_address_space: 35, - heap_start: chunk_align_down(unsafe { Address::from_usize(start) }), + heap_start: start, heap_end: chunk_align_up(unsafe { Address::from_usize(end) }), log_space_extent: 31, force_use_contiguous_spaces: false, From 0098a1968260fd412f6a268b8bec20e127429cb8 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 17 Oct 2024 18:28:27 +1300 Subject: [PATCH 7/8] Revert some changes --- .github/scripts/ci-test.sh | 2 +- src/util/memory.rs | 19 ------------------- .../mock_test_vm_layout_compressed_pointer.rs | 16 +++++++++------- 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/.github/scripts/ci-test.sh b/.github/scripts/ci-test.sh index 54182e9d4f..c25a22ec7c 100755 --- a/.github/scripts/ci-test.sh +++ b/.github/scripts/ci-test.sh @@ -34,7 +34,7 @@ find ./src ./tests -type f -name "mock_test_*" | while read -r file; do # Run the test with each plan it needs. for MMTK_PLAN in $PLANS; do - env MMTK_PLAN=$MMTK_PLAN cargo test --features mock_test,"$FEATURES" -- $t --nocapture; + env MMTK_PLAN=$MMTK_PLAN cargo test --features mock_test,"$FEATURES" -- $t; done done diff --git a/src/util/memory.rs b/src/util/memory.rs index 7f9c2135ef..771068aaff 100644 --- a/src/util/memory.rs +++ b/src/util/memory.rs @@ -356,25 +356,6 @@ pub(crate) fn get_system_total_memory() -> u64 { sys.total_memory() } -/// Find the given bytes that can be mmapped. This will do an anonymous mapping, record the return -/// address and then unmap the mapping. This is only used for testing. -#[cfg(test)] -pub fn find_usable_address(bytes: usize, align: usize) -> Option
{ - let flags = libc::MAP_ANON | libc::MAP_PRIVATE; - let prot = MmapStrategy::TEST.prot.into_native_flags(); - // Map extra to make sure that the return address is aligned. - let size_to_map = bytes + align; - let ret = unsafe { libc::mmap(std::ptr::null_mut(), size_to_map, prot, flags, -1, 0) }; - if ret as isize == -1 { - None - } else { - let usable_addr = Address::from_mut_ptr(ret); - munmap(usable_addr, size_to_map).unwrap(); - let ret = usable_addr.align_up(align); - Some(ret) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs b/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs index 2a1bf87755..4dec5d0806 100644 --- a/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs +++ b/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs @@ -4,7 +4,6 @@ use super::mock_test_prelude::*; use super::mock_test_vm_layout_default::test_with_vm_layout; use crate::util::conversions::*; use crate::util::heap::vm_layout::VMLayout; -use crate::util::heap::vm_layout::BYTES_IN_CHUNK; use crate::util::Address; // This test only run on 64bits. @@ -14,18 +13,21 @@ fn test_vm_layout_compressed_pointer() { with_mockvm( default_setup, || { + let start = if cfg!(target_os = "macos") { + // Impossible to map 0x4000_0000 on maocOS. SO choose a different address. + 0x20_0000_0000 + } else { + 0x4000_0000 + }; let heap_size = 1024 * 1024; - let start = crate::util::memory::find_usable_address(heap_size, BYTES_IN_CHUNK) - .expect("Cannot find usable address range"); - println!("Use {} as heap start", start); - let end = match start.as_usize() + heap_size { + let end = match start + heap_size { end if end <= (4usize << 30) => 4usize << 30, end if end <= (32usize << 30) => 32usize << 30, - _ => start.as_usize() + (32usize << 30), + _ => start + (32usize << 30), }; let layout = VMLayout { log_address_space: 35, - heap_start: start, + heap_start: chunk_align_down(unsafe { Address::from_usize(start) }), heap_end: chunk_align_up(unsafe { Address::from_usize(end) }), log_space_extent: 31, force_use_contiguous_spaces: false, From 44259e5221dc2a0886d8c1aa2f5c69abc5e0a8f9 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 17 Oct 2024 18:40:11 +1300 Subject: [PATCH 8/8] Try a range --- .../tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs b/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs index 4dec5d0806..658733f683 100644 --- a/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs +++ b/src/vm/tests/mock_tests/mock_test_vm_layout_compressed_pointer.rs @@ -15,7 +15,7 @@ fn test_vm_layout_compressed_pointer() { || { let start = if cfg!(target_os = "macos") { // Impossible to map 0x4000_0000 on maocOS. SO choose a different address. - 0x20_0000_0000 + 0x2_0000_0000 } else { 0x4000_0000 };