From e8a9d3a1b36d63c6275728521218505f8bf38a3f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 25 Sep 2019 08:29:42 -0700 Subject: [PATCH 1/2] Call `vm_deallocate` on OSX for CPU statistics Apparently StackOverflow doesn't have all the answers. The examples there I lifted this from didn't account to call `vm_deallocate` because it looks like we're handed allocated memory! Indeed running the previous state capture in a loop it infinitely allocated memory, but now it holds steady when called in a loop. --- src/cargo/util/cpu.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/cargo/util/cpu.rs b/src/cargo/util/cpu.rs index 3558b1e6400..6a3627b7a37 100644 --- a/src/cargo/util/cpu.rs +++ b/src/cargo/util/cpu.rs @@ -94,6 +94,10 @@ mod imp { type host_t = u32; type mach_port_t = u32; + type vm_map_t = mach_port_t; + type vm_offset_t = usize; + type vm_size_t = usize; + type vm_address_t = vm_offset_t; type processor_flavor_t = i32; type natural_t = u32; type processor_info_array_t = *mut i32; @@ -107,6 +111,8 @@ mod imp { const CPU_STATE_NICE: usize = 3; extern "C" { + static mut mach_task_self_: mach_port_t; + fn mach_host_self() -> mach_port_t; fn host_processor_info( host: host_t, @@ -115,6 +121,11 @@ mod imp { out_processor_info: *mut processor_info_array_t, out_processor_infoCnt: *mut mach_msg_type_number_t, ) -> kern_return_t; + fn vm_deallocate( + target_task: vm_map_t, + address: vm_address_t, + size: vm_size_t, + ) -> kern_return_t; } pub struct State { @@ -139,19 +150,24 @@ mod imp { if err != 0 { return Err(io::Error::last_os_error()); } - let cpu_info = slice::from_raw_parts(cpu_info, cpu_info_cnt as usize); + let cpu_info_slice = slice::from_raw_parts(cpu_info, cpu_info_cnt as usize); let mut ret = State { user: 0, system: 0, idle: 0, nice: 0, }; - for chunk in cpu_info.chunks(num_cpus_u as usize) { + for chunk in cpu_info_slice.chunks(num_cpus_u as usize) { ret.user += chunk[CPU_STATE_USER] as u64; ret.system += chunk[CPU_STATE_SYSTEM] as u64; ret.idle += chunk[CPU_STATE_IDLE] as u64; ret.nice += chunk[CPU_STATE_NICE] as u64; } + vm_deallocate( + mach_task_self_, + cpu_info as vm_address_t, + cpu_info_cnt as usize, + ); Ok(ret) } } From 0c812dbe963553986c8fd5508e66622c8de88a29 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 25 Sep 2019 08:37:55 -0700 Subject: [PATCH 2/2] Fix a panic collecting cpu data on OSX There's very little documentation on `host_processor_info` from what I can tell, so I'm just cribbing examples I've found elsewhere on the internet. It appears that I've misinterpreted one of the out parameters of `host_processor_info` as the number of elements of an array, but it actually is just something forwarded to `vm_deallocate`. --- src/cargo/util/cpu.rs | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/cargo/util/cpu.rs b/src/cargo/util/cpu.rs index 6a3627b7a37..453eb571906 100644 --- a/src/cargo/util/cpu.rs +++ b/src/cargo/util/cpu.rs @@ -90,7 +90,6 @@ mod imp { mod imp { use std::io; use std::ptr; - use std::slice; type host_t = u32; type mach_port_t = u32; @@ -109,6 +108,7 @@ mod imp { const CPU_STATE_SYSTEM: usize = 1; const CPU_STATE_IDLE: usize = 2; const CPU_STATE_NICE: usize = 3; + const CPU_STATE_MAX: usize = 4; extern "C" { static mut mach_task_self_: mach_port_t; @@ -135,39 +135,45 @@ mod imp { nice: u64, } + #[repr(C)] + struct processor_cpu_load_info_data_t { + cpu_ticks: [u32; CPU_STATE_MAX], + } + pub fn current() -> io::Result { + // There's scant little documentation on `host_processor_info` + // throughout the internet, so this is just modeled after what everyone + // else is doing. For now this is modeled largely after libuv. + unsafe { let mut num_cpus_u = 0; let mut cpu_info = ptr::null_mut(); - let mut cpu_info_cnt = 0; + let mut msg_type = 0; let err = host_processor_info( mach_host_self(), PROESSOR_CPU_LOAD_INFO, &mut num_cpus_u, &mut cpu_info, - &mut cpu_info_cnt, + &mut msg_type, ); if err != 0 { return Err(io::Error::last_os_error()); } - let cpu_info_slice = slice::from_raw_parts(cpu_info, cpu_info_cnt as usize); let mut ret = State { user: 0, system: 0, idle: 0, nice: 0, }; - for chunk in cpu_info_slice.chunks(num_cpus_u as usize) { - ret.user += chunk[CPU_STATE_USER] as u64; - ret.system += chunk[CPU_STATE_SYSTEM] as u64; - ret.idle += chunk[CPU_STATE_IDLE] as u64; - ret.nice += chunk[CPU_STATE_NICE] as u64; + let mut current = cpu_info as *const processor_cpu_load_info_data_t; + for _ in 0..num_cpus_u { + ret.user += (*current).cpu_ticks[CPU_STATE_USER] as u64; + ret.system += (*current).cpu_ticks[CPU_STATE_SYSTEM] as u64; + ret.idle += (*current).cpu_ticks[CPU_STATE_IDLE] as u64; + ret.nice += (*current).cpu_ticks[CPU_STATE_NICE] as u64; + current = current.offset(1); } - vm_deallocate( - mach_task_self_, - cpu_info as vm_address_t, - cpu_info_cnt as usize, - ); + vm_deallocate(mach_task_self_, cpu_info as vm_address_t, msg_type as usize); Ok(ret) } }