-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: implement process.cpuUsage
(Deno.cpuUsage
)
#27217
base: main
Are you sure you want to change the base?
Changes from all commits
4ded269
04c29de
781ed66
328406c
1a7f3ef
b9c82dc
42ccd16
4aba97b
c35ce58
7370938
5a4b688
853fa43
40ef9de
9bc31e9
d67ffeb
38fc5b4
e01c238
11c0d96
aeb8794
29d6514
1bb37b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,6 @@ use deno_core::OpState; | |
use deno_node::NODE_ENV_VAR_ALLOWLIST; | ||
use deno_path_util::normalize_path; | ||
use deno_permissions::PermissionsContainer; | ||
use serde::Serialize; | ||
use std::collections::HashMap; | ||
use std::env; | ||
|
||
|
@@ -31,6 +30,7 @@ deno_core::extension!( | |
op_get_exit_code, | ||
op_system_memory_info, | ||
op_uid, | ||
op_runtime_cpu_usage, | ||
op_runtime_memory_usage, | ||
], | ||
options = { | ||
|
@@ -60,6 +60,7 @@ deno_core::extension!( | |
op_get_exit_code, | ||
op_system_memory_info, | ||
op_uid, | ||
op_runtime_cpu_usage, | ||
op_runtime_memory_usage, | ||
], | ||
middleware = |op| match op.name { | ||
|
@@ -341,27 +342,127 @@ fn op_uid( | |
Ok(None) | ||
} | ||
|
||
// HeapStats stores values from a isolate.get_heap_statistics() call | ||
#[derive(Serialize)] | ||
#[serde(rename_all = "camelCase")] | ||
struct MemoryUsage { | ||
rss: usize, | ||
heap_total: usize, | ||
heap_used: usize, | ||
external: usize, | ||
#[op2] | ||
#[serde] | ||
fn op_runtime_cpu_usage() -> (usize, usize) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most of the other ops return a Struct, not just a tuple. While a tuple may be faster (I'm not sure how much performance matters here) consistency might be better. |
||
let (sys, user) = get_cpu_usage(); | ||
(sys.as_micros() as usize, user.as_micros() as usize) | ||
} | ||
|
||
#[cfg(unix)] | ||
fn get_cpu_usage() -> (std::time::Duration, std::time::Duration) { | ||
let mut rusage = std::mem::MaybeUninit::uninit(); | ||
|
||
// Uses POSIX getrusage from libc | ||
// to retrieve user and system times | ||
// SAFETY: libc call | ||
let ret = unsafe { libc::getrusage(libc::RUSAGE_SELF, rusage.as_mut_ptr()) }; | ||
if ret != 0 { | ||
return Default::default(); | ||
} | ||
|
||
// SAFETY: already checked the result | ||
let rusage = unsafe { rusage.assume_init() }; | ||
|
||
let sys = std::time::Duration::from_micros(rusage.ru_stime.tv_usec as u64) | ||
+ std::time::Duration::from_secs(rusage.ru_stime.tv_sec as u64); | ||
let user = std::time::Duration::from_micros(rusage.ru_utime.tv_usec as u64) | ||
+ std::time::Duration::from_secs(rusage.ru_utime.tv_sec as u64); | ||
|
||
(sys, user) | ||
} | ||
|
||
#[cfg(windows)] | ||
fn get_cpu_usage() -> (std::time::Duration, std::time::Duration) { | ||
use winapi::shared::minwindef::FALSE; | ||
use winapi::shared::minwindef::FILETIME; | ||
use winapi::shared::minwindef::TRUE; | ||
use winapi::um::minwinbase::SYSTEMTIME; | ||
use winapi::um::processthreadsapi::GetCurrentProcess; | ||
use winapi::um::processthreadsapi::GetProcessTimes; | ||
use winapi::um::timezoneapi::FileTimeToSystemTime; | ||
|
||
fn convert_system_time(system_time: SYSTEMTIME) -> std::time::Duration { | ||
std::time::Duration::from_secs( | ||
system_time.wHour as u64 * 3600 | ||
+ system_time.wMinute as u64 * 60 | ||
+ system_time.wSecond as u64, | ||
) + std::time::Duration::from_millis(system_time.wMilliseconds as u64) | ||
} | ||
|
||
let mut creation_time = std::mem::MaybeUninit::<FILETIME>::uninit(); | ||
let mut exit_time = std::mem::MaybeUninit::<FILETIME>::uninit(); | ||
let mut kernel_time = std::mem::MaybeUninit::<FILETIME>::uninit(); | ||
let mut user_time = std::mem::MaybeUninit::<FILETIME>::uninit(); | ||
|
||
// SAFETY: winapi calls | ||
let ret = unsafe { | ||
GetProcessTimes( | ||
GetCurrentProcess(), | ||
creation_time.as_mut_ptr(), | ||
exit_time.as_mut_ptr(), | ||
kernel_time.as_mut_ptr(), | ||
user_time.as_mut_ptr(), | ||
) | ||
}; | ||
|
||
if ret != TRUE { | ||
return std::default::Default::default(); | ||
} | ||
|
||
let mut kernel_system_time = std::mem::MaybeUninit::<SYSTEMTIME>::uninit(); | ||
let mut user_system_time = std::mem::MaybeUninit::<SYSTEMTIME>::uninit(); | ||
|
||
// SAFETY: convert to system time | ||
unsafe { | ||
let sys_ret = FileTimeToSystemTime( | ||
kernel_time.assume_init_mut(), | ||
kernel_system_time.as_mut_ptr(), | ||
); | ||
let user_ret = FileTimeToSystemTime( | ||
user_time.assume_init_mut(), | ||
user_system_time.as_mut_ptr(), | ||
); | ||
|
||
match (sys_ret, user_ret) { | ||
(TRUE, TRUE) => ( | ||
convert_system_time(kernel_system_time.assume_init()), | ||
convert_system_time(user_system_time.assume_init()), | ||
), | ||
(TRUE, FALSE) => ( | ||
convert_system_time(kernel_system_time.assume_init()), | ||
Default::default(), | ||
), | ||
(FALSE, TRUE) => ( | ||
Default::default(), | ||
convert_system_time(user_system_time.assume_init()), | ||
), | ||
(_, _) => Default::default(), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(not(any(windows, unix)))] | ||
fn get_cpu_usage() -> (std::time::Duration, std::time::Duration) { | ||
Default::default() | ||
} | ||
|
||
#[op2] | ||
#[serde] | ||
fn op_runtime_memory_usage(scope: &mut v8::HandleScope) -> MemoryUsage { | ||
fn op_runtime_memory_usage( | ||
scope: &mut v8::HandleScope, | ||
) -> (usize, usize, usize, usize) { | ||
let mut s = v8::HeapStatistics::default(); | ||
scope.get_heap_statistics(&mut s); | ||
MemoryUsage { | ||
rss: rss(), | ||
heap_total: s.total_heap_size(), | ||
heap_used: s.used_heap_size(), | ||
external: s.external_memory(), | ||
} | ||
|
||
let (rss, heap_total, heap_used, external) = ( | ||
rss(), | ||
s.total_heap_size(), | ||
s.used_heap_size(), | ||
s.external_memory(), | ||
); | ||
|
||
(rss, heap_total, heap_used, external) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I see you changed the |
||
} | ||
|
||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small nit, but maybe define this the same way that
memoryUsage
is defined. That is, create afunction
and then justProcess.prototype.memoryUsage = memoryUsage;
.I was going to suggest moving this next to
memoryUsage
too, but it seems that theprototypes
are mostly organized in alphabetical order (not all), so maybe leave it here.