summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/tests/unit_node/os_test.ts12
-rw-r--r--ext/node/lib.rs1
-rw-r--r--ext/node/ops/os/cpus.rs306
-rw-r--r--ext/node/ops/os/mod.rs90
-rw-r--r--ext/node/ops/os/priority.rs (renamed from ext/node/ops/os.rs)73
-rw-r--r--ext/node/polyfills/os.ts16
6 files changed, 408 insertions, 90 deletions
diff --git a/cli/tests/unit_node/os_test.ts b/cli/tests/unit_node/os_test.ts
index d687e9d0e..021cf5db1 100644
--- a/cli/tests/unit_node/os_test.ts
+++ b/cli/tests/unit_node/os_test.ts
@@ -241,13 +241,11 @@ Deno.test({
assertEquals(os.cpus().length, navigator.hardwareConcurrency);
for (const cpu of os.cpus()) {
- assertEquals(cpu.model, "");
- assertEquals(cpu.speed, 0);
- assertEquals(cpu.times.user, 0);
- assertEquals(cpu.times.nice, 0);
- assertEquals(cpu.times.sys, 0);
- assertEquals(cpu.times.idle, 0);
- assertEquals(cpu.times.irq, 0);
+ assert(cpu.model.length > 0);
+ assert(cpu.speed >= 0);
+ assert(cpu.times.user > 0);
+ assert(cpu.times.sys > 0);
+ assert(cpu.times.idle > 0);
}
},
});
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 0922c6986..36f0fede9 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -281,6 +281,7 @@ deno_core::extension!(deno_node,
ops::os::op_node_os_set_priority<P>,
ops::os::op_node_os_username<P>,
ops::os::op_geteuid<P>,
+ ops::os::op_cpus<P>,
op_node_build_os,
op_node_is_promise_rejected,
op_npm_process_state,
diff --git a/ext/node/ops/os/cpus.rs b/ext/node/ops/os/cpus.rs
new file mode 100644
index 000000000..5af55372f
--- /dev/null
+++ b/ext/node/ops/os/cpus.rs
@@ -0,0 +1,306 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::serde::Serialize;
+
+#[derive(Debug, Default, Serialize, Clone)]
+pub struct CpuTimes {
+ pub user: u64,
+ pub nice: u64,
+ pub sys: u64,
+ pub idle: u64,
+ pub irq: u64,
+}
+
+#[derive(Debug, Default, Serialize, Clone)]
+pub struct CpuInfo {
+ pub model: String,
+ /* in MHz */
+ pub speed: u64,
+ pub times: CpuTimes,
+}
+
+impl CpuInfo {
+ pub fn new() -> Self {
+ Self::default()
+ }
+}
+
+#[cfg(target_os = "macos")]
+pub fn cpu_info() -> Option<Vec<CpuInfo>> {
+ let mut model: [u8; 512] = [0; 512];
+ let mut size = std::mem::size_of_val(&model);
+
+ // Safety: Assumes correct behavior of platform-specific syscalls and data structures.
+ // Relies on specific sysctl names and sysconf parameter existence.
+ unsafe {
+ let ticks = libc::sysconf(libc::_SC_CLK_TCK);
+ let multiplier = 1000u64 / ticks as u64;
+ if libc::sysctlbyname(
+ "machdep.cpu.brand_string\0".as_ptr() as *const libc::c_char,
+ model.as_mut_ptr() as _,
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ ) != 0
+ && libc::sysctlbyname(
+ "hw.model\0".as_ptr() as *const libc::c_char,
+ model.as_mut_ptr() as _,
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ ) != 0
+ {
+ return None;
+ }
+
+ let mut cpu_speed: u64 = 0;
+ let mut cpu_speed_size = std::mem::size_of_val(&cpu_speed);
+
+ libc::sysctlbyname(
+ "hw.cpufrequency\0".as_ptr() as *const libc::c_char,
+ &mut cpu_speed as *mut _ as *mut libc::c_void,
+ &mut cpu_speed_size,
+ std::ptr::null_mut(),
+ 0,
+ );
+
+ if cpu_speed == 0 {
+ // https://github.com/libuv/libuv/pull/3679
+ //
+ // hw.cpufrequency sysctl seems to be missing on darwin/arm64
+ // so we instead hardcode a plausible value. This value matches
+ // what the mach kernel will report when running Rosetta apps.
+ cpu_speed = 2_400_000_000;
+ }
+
+ let mut num_cpus: libc::natural_t = 0;
+ let mut info: *mut libc::processor_cpu_load_info_data_t =
+ std::ptr::null_mut();
+ let mut msg_type: libc::mach_msg_type_number_t = 0;
+ if libc::host_processor_info(
+ libc::mach_host_self(),
+ libc::PROCESSOR_CPU_LOAD_INFO,
+ &mut num_cpus,
+ &mut info as *mut _ as *mut libc::processor_info_array_t,
+ &mut msg_type,
+ ) != 0
+ {
+ return None;
+ }
+
+ let mut cpus = vec![CpuInfo::new(); num_cpus as usize];
+
+ let info = std::slice::from_raw_parts(info, num_cpus as usize);
+ let model = std::ffi::CStr::from_ptr(model.as_ptr() as _)
+ .to_string_lossy()
+ .into_owned();
+ for (i, cpu) in cpus.iter_mut().enumerate() {
+ cpu.times.user =
+ info[i].cpu_ticks[libc::CPU_STATE_USER as usize] as u64 * multiplier;
+ cpu.times.nice =
+ info[i].cpu_ticks[libc::CPU_STATE_NICE as usize] as u64 * multiplier;
+ cpu.times.sys =
+ info[i].cpu_ticks[libc::CPU_STATE_SYSTEM as usize] as u64 * multiplier;
+ cpu.times.idle =
+ info[i].cpu_ticks[libc::CPU_STATE_IDLE as usize] as u64 * multiplier;
+
+ cpu.times.irq = 0;
+
+ cpu.model = model.clone();
+ cpu.speed = cpu_speed / 1000000;
+ }
+
+ libc::vm_deallocate(
+ libc::mach_task_self(),
+ info.as_ptr() as libc::vm_address_t,
+ msg_type as _,
+ );
+
+ Some(cpus)
+ }
+}
+
+#[cfg(target_os = "windows")]
+pub fn cpu_info() -> Option<Vec<CpuInfo>> {
+ use windows_sys::Win32::System::WindowsProgramming::NtQuerySystemInformation;
+ use windows_sys::Win32::System::WindowsProgramming::SystemProcessorPerformanceInformation;
+ use windows_sys::Win32::System::WindowsProgramming::SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
+
+ use std::os::windows::ffi::OsStrExt;
+ use std::os::windows::ffi::OsStringExt;
+
+ fn encode_wide(s: &str) -> Vec<u16> {
+ std::ffi::OsString::from(s)
+ .encode_wide()
+ .chain(Some(0))
+ .collect()
+ }
+
+ // Safety: Assumes correct behavior of platform-specific syscalls and data structures.
+ unsafe {
+ let mut system_info: winapi::um::sysinfoapi::SYSTEM_INFO =
+ std::mem::zeroed();
+ winapi::um::sysinfoapi::GetSystemInfo(&mut system_info);
+
+ let cpu_count = system_info.dwNumberOfProcessors as usize;
+
+ let mut cpus = vec![CpuInfo::new(); cpu_count];
+
+ let mut sppi: Vec<SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION> =
+ vec![std::mem::zeroed(); cpu_count];
+
+ let sppi_size = std::mem::size_of_val(&sppi[0]) * cpu_count;
+ let mut result_size = 0;
+
+ let status = NtQuerySystemInformation(
+ SystemProcessorPerformanceInformation,
+ sppi.as_mut_ptr() as *mut _,
+ sppi_size as u32,
+ &mut result_size,
+ );
+ if status != 0 {
+ return None;
+ }
+
+ assert_eq!(result_size, sppi_size as u32);
+
+ for i in 0..cpu_count {
+ let key_name =
+ format!("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\{}", i);
+ let key_name = encode_wide(&key_name);
+
+ let mut processor_key: windows_sys::Win32::System::Registry::HKEY =
+ std::mem::zeroed();
+ let err = windows_sys::Win32::System::Registry::RegOpenKeyExW(
+ windows_sys::Win32::System::Registry::HKEY_LOCAL_MACHINE,
+ key_name.as_ptr(),
+ 0,
+ windows_sys::Win32::System::Registry::KEY_QUERY_VALUE,
+ &mut processor_key,
+ );
+
+ if err != 0 {
+ return None;
+ }
+
+ let mut cpu_speed = 0;
+ let mut cpu_speed_size = std::mem::size_of_val(&cpu_speed) as u32;
+
+ let err = windows_sys::Win32::System::Registry::RegQueryValueExW(
+ processor_key,
+ encode_wide("~MHz").as_ptr() as *mut _,
+ std::ptr::null_mut(),
+ std::ptr::null_mut(),
+ &mut cpu_speed as *mut _ as *mut _,
+ &mut cpu_speed_size,
+ );
+
+ if err != 0 {
+ return None;
+ }
+
+ let cpu_brand: [u16; 512] = [0; 512];
+ let mut cpu_brand_size = std::mem::size_of_val(&cpu_brand) as u32;
+
+ let err = windows_sys::Win32::System::Registry::RegQueryValueExW(
+ processor_key,
+ encode_wide("ProcessorNameString").as_ptr() as *mut _,
+ std::ptr::null_mut(),
+ std::ptr::null_mut(),
+ cpu_brand.as_ptr() as *mut _,
+ &mut cpu_brand_size,
+ );
+ windows_sys::Win32::System::Registry::RegCloseKey(processor_key);
+
+ if err != 0 {
+ return None;
+ }
+
+ let cpu_brand =
+ std::ffi::OsString::from_wide(&cpu_brand[..cpu_brand_size as usize])
+ .into_string()
+ .unwrap();
+
+ cpus[i].model = cpu_brand;
+ cpus[i].speed = cpu_speed as u64;
+
+ cpus[i].times.user = sppi[i].UserTime as u64 / 10000;
+ cpus[i].times.sys =
+ (sppi[i].KernelTime - sppi[i].IdleTime) as u64 / 10000;
+ cpus[i].times.idle = sppi[i].IdleTime as u64 / 10000;
+ /* InterruptTime is Reserved1[1] */
+ cpus[i].times.irq = sppi[i].Reserved1[1] as u64 / 10000;
+ cpus[i].times.nice = 0;
+ }
+ Some(cpus)
+ }
+}
+
+#[cfg(target_os = "linux")]
+pub fn cpu_info() -> Option<Vec<CpuInfo>> {
+ use std::io::BufRead;
+
+ let mut cpus = vec![CpuInfo::new(); 8192]; /* Kernel maxmimum */
+
+ let fp = std::fs::File::open("/proc/stat").ok()?;
+ let reader = std::io::BufReader::new(fp);
+
+ for (i, line) in reader.lines().enumerate() {
+ let line = line.ok()?;
+ if !line.starts_with("cpu") {
+ break;
+ }
+ let mut fields = line.split_whitespace();
+ fields.next()?;
+ let user = fields.next()?.parse::<u64>().ok()?;
+ let nice = fields.next()?.parse::<u64>().ok()?;
+ let sys = fields.next()?.parse::<u64>().ok()?;
+ let idle = fields.next()?.parse::<u64>().ok()?;
+ let irq = fields.next()?.parse::<u64>().ok()?;
+
+ cpus[i].times.user = user;
+ cpus[i].times.nice = nice;
+ cpus[i].times.sys = sys;
+ cpus[i].times.idle = idle;
+ cpus[i].times.irq = irq;
+ }
+
+ let fp = std::fs::File::open("/proc/cpuinfo").ok()?;
+ let reader = std::io::BufReader::new(fp);
+
+ let mut i = 0;
+ for line in reader.lines() {
+ let line = line.ok()?;
+ if !line.starts_with("model name") {
+ continue;
+ }
+ let mut fields = line.splitn(2, ':');
+ fields.next()?;
+ let model = fields.next()?.trim();
+
+ cpus[i].model = model.to_string();
+ i += 1;
+ }
+
+ cpus.truncate(i);
+ Some(cpus)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_cpu_info() {
+ let info = cpu_info();
+ assert!(info.is_some());
+ let info = info.unwrap();
+ assert!(!info.is_empty());
+ for cpu in info {
+ assert!(!cpu.model.is_empty());
+ assert!(cpu.times.user > 0);
+ assert!(cpu.times.sys > 0);
+ assert!(cpu.times.idle > 0);
+ }
+ }
+}
diff --git a/ext/node/ops/os/mod.rs b/ext/node/ops/os/mod.rs
new file mode 100644
index 000000000..4fadc1ff8
--- /dev/null
+++ b/ext/node/ops/os/mod.rs
@@ -0,0 +1,90 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use crate::NodePermissions;
+use deno_core::error::type_error;
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::OpState;
+
+mod cpus;
+mod priority;
+
+#[op2(fast)]
+pub fn op_node_os_get_priority<P>(
+ state: &mut OpState,
+ pid: u32,
+) -> Result<i32, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ {
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_sys("getPriority", "node:os.getPriority()")?;
+ }
+
+ priority::get_priority(pid)
+}
+
+#[op2(fast)]
+pub fn op_node_os_set_priority<P>(
+ state: &mut OpState,
+ pid: u32,
+ priority: i32,
+) -> Result<(), AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ {
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_sys("setPriority", "node:os.setPriority()")?;
+ }
+
+ priority::set_priority(pid, priority)
+}
+
+#[op2]
+#[string]
+pub fn op_node_os_username<P>(state: &mut OpState) -> Result<String, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ {
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_sys("userInfo", "node:os.userInfo()")?;
+ }
+
+ Ok(deno_whoami::username())
+}
+
+#[op2(fast)]
+pub fn op_geteuid<P>(state: &mut OpState) -> Result<u32, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ {
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_sys("geteuid", "node:os.geteuid()")?;
+ }
+
+ #[cfg(windows)]
+ let euid = 0;
+ #[cfg(unix)]
+ // SAFETY: Call to libc geteuid.
+ let euid = unsafe { libc::geteuid() };
+
+ Ok(euid)
+}
+
+#[op2]
+#[serde]
+pub fn op_cpus<P>(state: &mut OpState) -> Result<Vec<cpus::CpuInfo>, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ {
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_sys("cpus", "node:os.cpus()")?;
+ }
+
+ cpus::cpu_info().ok_or_else(|| type_error("Failed to get cpu info"))
+}
diff --git a/ext/node/ops/os.rs b/ext/node/ops/os/priority.rs
index 4f6f56f31..2d5994705 100644
--- a/ext/node/ops/os.rs
+++ b/ext/node/ops/os/priority.rs
@@ -1,78 +1,11 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-use crate::NodePermissions;
use deno_core::error::AnyError;
-use deno_core::op2;
-use deno_core::OpState;
-#[op2(fast)]
-pub fn op_node_os_get_priority<P>(
- state: &mut OpState,
- pid: u32,
-) -> Result<i32, AnyError>
-where
- P: NodePermissions + 'static,
-{
- {
- let permissions = state.borrow_mut::<P>();
- permissions.check_sys("getPriority", "node:os.getPriority()")?;
- }
-
- priority::get_priority(pid)
-}
-
-#[op2(fast)]
-pub fn op_node_os_set_priority<P>(
- state: &mut OpState,
- pid: u32,
- priority: i32,
-) -> Result<(), AnyError>
-where
- P: NodePermissions + 'static,
-{
- {
- let permissions = state.borrow_mut::<P>();
- permissions.check_sys("setPriority", "node:os.setPriority()")?;
- }
-
- priority::set_priority(pid, priority)
-}
-
-#[op2]
-#[string]
-pub fn op_node_os_username<P>(state: &mut OpState) -> Result<String, AnyError>
-where
- P: NodePermissions + 'static,
-{
- {
- let permissions = state.borrow_mut::<P>();
- permissions.check_sys("userInfo", "node:os.userInfo()")?;
- }
-
- Ok(deno_whoami::username())
-}
-
-#[op2(fast)]
-pub fn op_geteuid<P>(state: &mut OpState) -> Result<u32, AnyError>
-where
- P: NodePermissions + 'static,
-{
- {
- let permissions = state.borrow_mut::<P>();
- permissions.check_sys("geteuid", "node:os.geteuid()")?;
- }
-
- #[cfg(windows)]
- let euid = 0;
- #[cfg(unix)]
- // SAFETY: Call to libc geteuid.
- let euid = unsafe { libc::geteuid() };
-
- Ok(euid)
-}
+pub use impl_::*;
#[cfg(unix)]
-mod priority {
+mod impl_ {
use super::*;
use errno::errno;
use errno::set_errno;
@@ -106,7 +39,7 @@ mod priority {
}
#[cfg(windows)]
-mod priority {
+mod impl_ {
use super::*;
use deno_core::error::type_error;
use winapi::shared::minwindef::DWORD;
diff --git a/ext/node/polyfills/os.ts b/ext/node/polyfills/os.ts
index d1f5e7bfa..283ce5a0b 100644
--- a/ext/node/polyfills/os.ts
+++ b/ext/node/polyfills/os.ts
@@ -32,6 +32,8 @@ import { os } from "ext:deno_node/internal_binding/constants.ts";
import { osUptime } from "ext:runtime/30_os.js";
import { Buffer } from "ext:deno_node/internal/buffer.mjs";
+const ops = core.ops;
+
export const constants = os;
interface CPUTimes {
@@ -129,19 +131,7 @@ export function arch(): string {
(uptime as any)[Symbol.toPrimitive] = (): number => uptime();
export function cpus(): CPUCoreInfo[] {
- return Array.from(Array(navigator.hardwareConcurrency), () => {
- return {
- model: "",
- speed: 0,
- times: {
- user: 0,
- nice: 0,
- sys: 0,
- idle: 0,
- irq: 0,
- },
- };
- });
+ return ops.op_cpus();
}
/**