Skip to content

Commit e6dda60

Browse files
authored
feat: implement process.cpuUsage (Deno.cpuUsage) (#27217)
1 parent b7456fe commit e6dda60

File tree

4 files changed

+136
-21
lines changed

4 files changed

+136
-21
lines changed

ext/node/polyfills/process.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -441,11 +441,7 @@ Process.prototype.config = {
441441
};
442442

443443
Process.prototype.cpuUsage = function () {
444-
warnNotImplemented("process.cpuUsage()");
445-
return {
446-
user: 0,
447-
system: 0,
448-
};
444+
return Deno.cpuUsage();
449445
};
450446

451447
/** https://nodejs.org/api/process.html#process_process_cwd */

ext/os/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,11 @@ This crate implements OS specific APIs for Deno
3232
| Linux | sysinfo and `/proc/meminfo` | - |
3333
| Windows | `sysinfoapi::GlobalMemoryStatusEx` | - |
3434
| macOS | <br> <pre> sysctl([CTL_HW, HW_MEMSIZE]); <br> sysctl([CTL_VM, VM_SWAPUSAGE]); <br> host_statistics64(mach_host_self(), HOST_VM_INFO64) </pre> | - |
35+
36+
`cpu_usage`
37+
38+
| Target family | Syscall | Description |
39+
| ------------- | ------------------------------------ | ----------- |
40+
| Linux | getrusage | - |
41+
| Windows | `processthreadsapi::GetProcessTimes` | - |
42+
| macOS | getrusage | - |

ext/os/lib.rs

+117-15
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ deno_core::extension!(
6969
op_get_exit_code,
7070
op_system_memory_info,
7171
op_uid,
72+
op_runtime_cpu_usage,
7273
op_runtime_memory_usage,
7374
ops::signal::op_signal_bind,
7475
ops::signal::op_signal_unbind,
@@ -106,6 +107,7 @@ deno_core::extension!(
106107
op_get_exit_code,
107108
op_system_memory_info,
108109
op_uid,
110+
op_runtime_cpu_usage,
109111
op_runtime_memory_usage,
110112
ops::signal::op_signal_bind,
111113
ops::signal::op_signal_unbind,
@@ -420,27 +422,127 @@ fn op_uid(state: &mut OpState) -> Result<Option<u32>, PermissionCheckError> {
420422
Ok(None)
421423
}
422424

423-
// HeapStats stores values from a isolate.get_heap_statistics() call
424-
#[derive(Serialize)]
425-
#[serde(rename_all = "camelCase")]
426-
struct MemoryUsage {
427-
rss: usize,
428-
heap_total: usize,
429-
heap_used: usize,
430-
external: usize,
425+
#[op2]
426+
#[serde]
427+
fn op_runtime_cpu_usage() -> (usize, usize) {
428+
let (sys, user) = get_cpu_usage();
429+
(sys.as_micros() as usize, user.as_micros() as usize)
430+
}
431+
432+
#[cfg(unix)]
433+
fn get_cpu_usage() -> (std::time::Duration, std::time::Duration) {
434+
let mut rusage = std::mem::MaybeUninit::uninit();
435+
436+
// Uses POSIX getrusage from libc
437+
// to retrieve user and system times
438+
// SAFETY: libc call
439+
let ret = unsafe { libc::getrusage(libc::RUSAGE_SELF, rusage.as_mut_ptr()) };
440+
if ret != 0 {
441+
return Default::default();
442+
}
443+
444+
// SAFETY: already checked the result
445+
let rusage = unsafe { rusage.assume_init() };
446+
447+
let sys = std::time::Duration::from_micros(rusage.ru_stime.tv_usec as u64)
448+
+ std::time::Duration::from_secs(rusage.ru_stime.tv_sec as u64);
449+
let user = std::time::Duration::from_micros(rusage.ru_utime.tv_usec as u64)
450+
+ std::time::Duration::from_secs(rusage.ru_utime.tv_sec as u64);
451+
452+
(sys, user)
453+
}
454+
455+
#[cfg(windows)]
456+
fn get_cpu_usage() -> (std::time::Duration, std::time::Duration) {
457+
use winapi::shared::minwindef::FALSE;
458+
use winapi::shared::minwindef::FILETIME;
459+
use winapi::shared::minwindef::TRUE;
460+
use winapi::um::minwinbase::SYSTEMTIME;
461+
use winapi::um::processthreadsapi::GetCurrentProcess;
462+
use winapi::um::processthreadsapi::GetProcessTimes;
463+
use winapi::um::timezoneapi::FileTimeToSystemTime;
464+
465+
fn convert_system_time(system_time: SYSTEMTIME) -> std::time::Duration {
466+
std::time::Duration::from_secs(
467+
system_time.wHour as u64 * 3600
468+
+ system_time.wMinute as u64 * 60
469+
+ system_time.wSecond as u64,
470+
) + std::time::Duration::from_millis(system_time.wMilliseconds as u64)
471+
}
472+
473+
let mut creation_time = std::mem::MaybeUninit::<FILETIME>::uninit();
474+
let mut exit_time = std::mem::MaybeUninit::<FILETIME>::uninit();
475+
let mut kernel_time = std::mem::MaybeUninit::<FILETIME>::uninit();
476+
let mut user_time = std::mem::MaybeUninit::<FILETIME>::uninit();
477+
478+
// SAFETY: winapi calls
479+
let ret = unsafe {
480+
GetProcessTimes(
481+
GetCurrentProcess(),
482+
creation_time.as_mut_ptr(),
483+
exit_time.as_mut_ptr(),
484+
kernel_time.as_mut_ptr(),
485+
user_time.as_mut_ptr(),
486+
)
487+
};
488+
489+
if ret != TRUE {
490+
return std::default::Default::default();
491+
}
492+
493+
let mut kernel_system_time = std::mem::MaybeUninit::<SYSTEMTIME>::uninit();
494+
let mut user_system_time = std::mem::MaybeUninit::<SYSTEMTIME>::uninit();
495+
496+
// SAFETY: convert to system time
497+
unsafe {
498+
let sys_ret = FileTimeToSystemTime(
499+
kernel_time.assume_init_mut(),
500+
kernel_system_time.as_mut_ptr(),
501+
);
502+
let user_ret = FileTimeToSystemTime(
503+
user_time.assume_init_mut(),
504+
user_system_time.as_mut_ptr(),
505+
);
506+
507+
match (sys_ret, user_ret) {
508+
(TRUE, TRUE) => (
509+
convert_system_time(kernel_system_time.assume_init()),
510+
convert_system_time(user_system_time.assume_init()),
511+
),
512+
(TRUE, FALSE) => (
513+
convert_system_time(kernel_system_time.assume_init()),
514+
Default::default(),
515+
),
516+
(FALSE, TRUE) => (
517+
Default::default(),
518+
convert_system_time(user_system_time.assume_init()),
519+
),
520+
(_, _) => Default::default(),
521+
}
522+
}
523+
}
524+
525+
#[cfg(not(any(windows, unix)))]
526+
fn get_cpu_usage() -> (std::time::Duration, std::time::Duration) {
527+
Default::default()
431528
}
432529

433530
#[op2]
434531
#[serde]
435-
fn op_runtime_memory_usage(scope: &mut v8::HandleScope) -> MemoryUsage {
532+
fn op_runtime_memory_usage(
533+
scope: &mut v8::HandleScope,
534+
) -> (usize, usize, usize, usize) {
436535
let mut s = v8::HeapStatistics::default();
437536
scope.get_heap_statistics(&mut s);
438-
MemoryUsage {
439-
rss: rss(),
440-
heap_total: s.total_heap_size(),
441-
heap_used: s.used_heap_size(),
442-
external: s.external_memory(),
443-
}
537+
538+
let (rss, heap_total, heap_used, external) = (
539+
rss(),
540+
s.total_heap_size(),
541+
s.used_heap_size(),
542+
s.external_memory(),
543+
);
544+
545+
(rss, heap_total, heap_used, external)
444546
}
445547

446548
#[cfg(any(target_os = "android", target_os = "linux"))]

runtime/js/90_deno_ns.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { core, primordials } from "ext:core/mod.js";
44
import {
55
op_net_listen_udp,
66
op_net_listen_unixpacket,
7+
op_runtime_cpu_usage,
78
op_runtime_memory_usage,
89
} from "ext:core/ops";
910

@@ -59,7 +60,15 @@ const denoNs = {
5960
makeTempDir: fs.makeTempDir,
6061
makeTempFileSync: fs.makeTempFileSync,
6162
makeTempFile: fs.makeTempFile,
62-
memoryUsage: () => op_runtime_memory_usage(),
63+
cpuUsage: () => {
64+
const { 0: system, 1: user } = op_runtime_cpu_usage();
65+
return { system, user };
66+
},
67+
memoryUsage: () => {
68+
const { 0: rss, 1: heapTotal, 2: heapUsed, 3: external } =
69+
op_runtime_memory_usage();
70+
return { rss, heapTotal, heapUsed, external };
71+
},
6372
mkdirSync: fs.mkdirSync,
6473
mkdir: fs.mkdir,
6574
chdir: fs.chdir,

0 commit comments

Comments
 (0)