Skip to content
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(core): Deno.core.heapStats() #9659

Merged
merged 8 commits into from
Mar 23, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions core/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ lazy_static! {
v8::ExternalReference {
function: get_proxy_details.map_fn_to()
},
v8::ExternalReference {
function: heap_stats.map_fn_to(),
},
]);
}

Expand Down Expand Up @@ -135,6 +138,7 @@ pub fn initialize_context<'s>(
set_func(scope, core_val, "deserialize", deserialize);
set_func(scope, core_val, "getPromiseDetails", get_promise_details);
set_func(scope, core_val, "getProxyDetails", get_proxy_details);
set_func(scope, core_val, "heapStats", heap_stats);

let shared_key = v8::String::new(scope, "shared").unwrap();
core_val.set_accessor(scope, shared_key.into(), shared_getter);
Expand Down Expand Up @@ -920,3 +924,102 @@ fn throw_type_error(scope: &mut v8::HandleScope, message: impl AsRef<str>) {
let exception = v8::Exception::type_error(scope, message);
scope.throw_exception(exception);
}

fn heap_stats(
scope: &mut v8::HandleScope,
_args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
fn set_prop(
scope: &mut v8::HandleScope,
obj: v8::Local<v8::Object>,
name: &'static str,
value: usize,
) {
let key = v8::String::new(scope, name).unwrap();
let val = v8::Number::new(scope, value as f64);
obj.set(scope, key.into(), val.into());
}

let s = get_heap_stats(scope);

// TODO: use serde for this once we have serde_v8
let obj = v8::Object::new(scope);
set_prop(scope, obj, "total_heap_size", s.total_heap_size);
set_prop(
scope,
obj,
"total_heap_size_executable",
s.total_heap_size_executable,
);
set_prop(scope, obj, "total_physical_size", s.total_physical_size);
set_prop(scope, obj, "total_available_size", s.total_available_size);
set_prop(
scope,
obj,
"total_global_handles_size",
s.total_global_handles_size,
);
set_prop(
scope,
obj,
"used_global_handles_size",
s.used_global_handles_size,
);
set_prop(scope, obj, "used_heap_size", s.used_heap_size);
set_prop(scope, obj, "heap_size_limit", s.heap_size_limit);
set_prop(scope, obj, "malloced_memory", s.malloced_memory);
set_prop(scope, obj, "external_memory", s.external_memory);
set_prop(scope, obj, "peak_malloced_memory", s.peak_malloced_memory);
set_prop(
scope,
obj,
"number_of_native_contexts",
s.number_of_native_contexts,
);
set_prop(
scope,
obj,
"number_of_detached_contexts",
s.number_of_detached_contexts,
);

rv.set(obj.into());
}

// HeapStats stores values from a isolate.get_heap_statistics() call
struct HeapStats {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you're wrapping v8::HeapStatistics because that makes it easier to serialize in the future?

Copy link
Contributor Author

@AaronO AaronO Mar 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly, with serde_v8 (and the new op-ABI) all the set_prop() calls would become a one-liner to_v8() and by keeping it in a struct we can have serde handle the camelCase-ification (if desired).

There are other core binding funcs that will be simplified with serde_v8, e.g: get_proxy_details and get_promise_details conceptually return tuples, which serde_v8 can serialize to JS arrays (removing a fair amount of boilerplate in each function)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could probably add some simple alloc-dealloc tests (of large arrays) to ensure they are correctly reflected in the metrics.

Anything else that warrants testing in your opinion ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AaronO such test would be sufficient

total_heap_size: usize,
total_heap_size_executable: usize,
total_physical_size: usize,
total_available_size: usize,
total_global_handles_size: usize,
used_global_handles_size: usize,
used_heap_size: usize,
heap_size_limit: usize,
malloced_memory: usize,
external_memory: usize,
peak_malloced_memory: usize,
number_of_native_contexts: usize,
number_of_detached_contexts: usize,
}
fn get_heap_stats(isolate: &mut v8::Isolate) -> HeapStats {
let mut s = v8::HeapStatistics::default();
isolate.get_heap_statistics(&mut s);

HeapStats {
total_heap_size: s.total_heap_size(),
total_heap_size_executable: s.total_heap_size_executable(),
total_physical_size: s.total_physical_size(),
total_available_size: s.total_available_size(),
total_global_handles_size: s.total_global_handles_size(),
used_global_handles_size: s.used_global_handles_size(),
used_heap_size: s.used_heap_size(),
heap_size_limit: s.heap_size_limit(),
malloced_memory: s.malloced_memory(),
external_memory: s.external_memory(),
peak_malloced_memory: s.peak_malloced_memory(),
number_of_native_contexts: s.number_of_native_contexts(),
number_of_detached_contexts: s.number_of_detached_contexts(),
}
}
3 changes: 3 additions & 0 deletions core/lib.deno_core.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,8 @@ declare namespace Deno {

/** Close the resource with the specified op id. */
function close(rid: number): void;

/** Get heap stats for current isolate/worker */
function heapStats(): Record<string, number>;
}
}