summaryrefslogtreecommitdiff
path: root/runtime/ops/os/mod.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2022-12-17 17:25:51 -0500
committerGitHub <noreply@github.com>2022-12-17 23:25:51 +0100
commite9ecfdd20ac3f8a9d920bbcc4e5dd25bd262b4b7 (patch)
tree7604e5e42823656d5378d60791a4f68b66f7ca69 /runtime/ops/os/mod.rs
parentf46df3e35940fc78163945eed33e58fafed0b06b (diff)
fix(runtime): `Deno.memoryUsage().rss` should return correct value (#17088)
This commit changes implementation of "Deno.memoryUsage()" to return correct value for "rss" field. To do that we implement a specialized function per os to retrieve this information.
Diffstat (limited to 'runtime/ops/os/mod.rs')
-rw-r--r--runtime/ops/os/mod.rs128
1 files changed, 127 insertions, 1 deletions
diff --git a/runtime/ops/os/mod.rs b/runtime/ops/os/mod.rs
index b93935955..613b4507d 100644
--- a/runtime/ops/os/mod.rs
+++ b/runtime/ops/os/mod.rs
@@ -4,11 +4,14 @@ use super::utils::into_string;
use crate::permissions::Permissions;
use crate::worker::ExitCode;
use deno_core::error::{type_error, AnyError};
+use deno_core::op;
use deno_core::url::Url;
+use deno_core::v8;
use deno_core::Extension;
+use deno_core::ExtensionBuilder;
use deno_core::OpState;
-use deno_core::{op, ExtensionBuilder};
use deno_node::NODE_ENV_VAR_ALLOWLIST;
+use serde::Serialize;
use std::collections::HashMap;
use std::env;
@@ -30,6 +33,7 @@ fn init_ops(builder: &mut ExtensionBuilder) -> &mut ExtensionBuilder {
op_set_exit_code::decl(),
op_system_memory_info::decl(),
op_uid::decl(),
+ op_runtime_memory_usage::decl(),
])
}
@@ -297,3 +301,125 @@ fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
.check("uid", Some("Deno.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,
+}
+
+#[op(v8)]
+fn op_runtime_memory_usage(scope: &mut v8::HandleScope) -> MemoryUsage {
+ 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(),
+ }
+}
+
+#[cfg(target_os = "linux")]
+fn rss() -> usize {
+ // Inspired by https://github.com/Arc-blroth/memory-stats/blob/5364d0d09143de2a470d33161b2330914228fde9/src/linux.rs
+
+ // Extracts a positive integer from a string that
+ // may contain leading spaces and trailing chars.
+ // Returns the extracted number and the index of
+ // the next character in the string.
+ fn scan_int(string: &str) -> (usize, usize) {
+ let mut out = 0;
+ let mut idx = 0;
+ let mut chars = string.chars().peekable();
+ while let Some(' ') = chars.next_if_eq(&' ') {
+ idx += 1;
+ }
+ for n in chars {
+ idx += 1;
+ if ('0'..='9').contains(&n) {
+ out *= 10;
+ out += n as usize - '0' as usize;
+ } else {
+ break;
+ }
+ }
+ (out, idx)
+ }
+
+ let statm_content = if let Ok(c) = std::fs::read_to_string("/proc/self/statm")
+ {
+ c
+ } else {
+ return 0;
+ };
+
+ // statm returns the virtual size and rss, in
+ // multiples of the page size, as the first
+ // two columns of output.
+ // SAFETY: libc call
+ let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
+
+ if page_size < 0 {
+ return 0;
+ }
+
+ let (_total_size_pages, idx) = scan_int(&statm_content);
+ let (total_rss_pages, _) = scan_int(&statm_content[idx..]);
+
+ total_rss_pages * page_size as usize
+}
+
+#[cfg(target_os = "macos")]
+fn rss() -> usize {
+ // Inspired by https://github.com/Arc-blroth/memory-stats/blob/5364d0d09143de2a470d33161b2330914228fde9/src/darwin.rs
+
+ let mut task_info =
+ std::mem::MaybeUninit::<libc::mach_task_basic_info_data_t>::uninit();
+ let mut count = libc::MACH_TASK_BASIC_INFO_COUNT;
+ // SAFETY: libc calls
+ let r = unsafe {
+ libc::task_info(
+ libc::mach_task_self(),
+ libc::MACH_TASK_BASIC_INFO,
+ task_info.as_mut_ptr() as libc::task_info_t,
+ &mut count as *mut libc::mach_msg_type_number_t,
+ )
+ };
+ // According to libuv this should never fail
+ assert_eq!(r, libc::KERN_SUCCESS);
+ // SAFETY: we just asserted that it was success
+ let task_info = unsafe { task_info.assume_init() };
+ task_info.resident_size as usize
+}
+
+#[cfg(windows)]
+fn rss() -> usize {
+ use winapi::shared::minwindef::DWORD;
+ use winapi::shared::minwindef::FALSE;
+ use winapi::um::processthreadsapi::GetCurrentProcess;
+ use winapi::um::psapi::GetProcessMemoryInfo;
+ use winapi::um::psapi::PROCESS_MEMORY_COUNTERS;
+
+ // SAFETY: winapi calls
+ unsafe {
+ // this handle is a constant—no need to close it
+ let current_process = GetCurrentProcess();
+ let mut pmc: PROCESS_MEMORY_COUNTERS = std::mem::zeroed();
+
+ if GetProcessMemoryInfo(
+ current_process,
+ &mut pmc,
+ std::mem::size_of::<PROCESS_MEMORY_COUNTERS>() as DWORD,
+ ) != FALSE
+ {
+ pmc.WorkingSetSize
+ } else {
+ 0
+ }
+ }
+}