1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

feat(core): Deno.core.heapStats() (#9659)

This commit implements "Deno.core.heapStats()" function 
that allows to programatically measure isolate heap-usage.
This commit is contained in:
Aaron O'Mullan 2021-03-23 15:33:06 +01:00 committed by GitHub
parent 26f7a3f185
commit 876f075dde
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 149 additions and 0 deletions

36
cli/tests/heapstats.js Normal file
View file

@ -0,0 +1,36 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
"use strict";
function allocTest(alloc, allocAssert, deallocAssert) {
// Helper func that GCs then returns heapStats
const sample = () => {
// deno-lint-ignore no-undef
gc();
return Deno.core.heapStats();
};
const delta = (t1, t2) => t2.usedHeapSize - t1.usedHeapSize;
// Sample "clean" heapStats
const t1 = sample();
// Alloc
let x = alloc();
const t2 = sample();
allocAssert(delta(t1, t2));
// Free
x = null;
const t3 = sample();
deallocAssert(delta(t2, t3));
}
function main() {
// Large-array test, 1M slot array consumes ~4MB (4B per slot)
allocTest(
() => new Array(1e6),
(delta) => console.log("Allocated:", Math.round(delta / 1e6) + "MB"),
(delta) => console.log("Freed:", Math.round(delta / 1e6) + "MB"),
);
}
main();

View file

@ -0,0 +1,2 @@
Allocated: 4MB
Freed: -4MB

View file

@ -3217,6 +3217,11 @@ console.log("finish");
output: "exit_error42.ts.out", output: "exit_error42.ts.out",
}); });
itest!(heapstats {
args: "run --quiet --v8-flags=--expose-gc heapstats.js",
output: "heapstats.js.out",
});
itest!(https_import { itest!(https_import {
args: "run --quiet --reload --cert tls/RootCA.pem https_import.ts", args: "run --quiet --reload --cert tls/RootCA.pem https_import.ts",
output: "https_import.ts.out", output: "https_import.ts.out",

View file

@ -59,6 +59,9 @@ lazy_static! {
v8::ExternalReference { v8::ExternalReference {
function: get_proxy_details.map_fn_to() function: get_proxy_details.map_fn_to()
}, },
v8::ExternalReference {
function: heap_stats.map_fn_to(),
},
]); ]);
} }
@ -135,6 +138,7 @@ pub fn initialize_context<'s>(
set_func(scope, core_val, "deserialize", deserialize); set_func(scope, core_val, "deserialize", deserialize);
set_func(scope, core_val, "getPromiseDetails", get_promise_details); set_func(scope, core_val, "getPromiseDetails", get_promise_details);
set_func(scope, core_val, "getProxyDetails", get_proxy_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(); let shared_key = v8::String::new(scope, "shared").unwrap();
core_val.set_accessor(scope, shared_key.into(), shared_getter); core_val.set_accessor(scope, shared_key.into(), shared_getter);
@ -923,3 +927,102 @@ fn throw_type_error(scope: &mut v8::HandleScope, message: impl AsRef<str>) {
let exception = v8::Exception::type_error(scope, message); let exception = v8::Exception::type_error(scope, message);
scope.throw_exception(exception); 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, "totalHeapSize", s.total_heap_size);
set_prop(
scope,
obj,
"totalHeapSizexecutable",
s.total_heap_size_executable,
);
set_prop(scope, obj, "totalPhysicalSize", s.total_physical_size);
set_prop(scope, obj, "totalAvailableSize", s.total_available_size);
set_prop(
scope,
obj,
"totalGlobalHandlesSize",
s.total_global_handles_size,
);
set_prop(
scope,
obj,
"usedGlobalHandlesSize",
s.used_global_handles_size,
);
set_prop(scope, obj, "usedHeapSize", s.used_heap_size);
set_prop(scope, obj, "heapSizeLimit", s.heap_size_limit);
set_prop(scope, obj, "mallocedMemory", s.malloced_memory);
set_prop(scope, obj, "externalMemory", s.external_memory);
set_prop(scope, obj, "peakMallocedMemory", s.peak_malloced_memory);
set_prop(
scope,
obj,
"numberOfNativeContexts",
s.number_of_native_contexts,
);
set_prop(
scope,
obj,
"numberOfDetachedContexts",
s.number_of_detached_contexts,
);
rv.set(obj.into());
}
// HeapStats stores values from a isolate.get_heap_statistics() call
struct HeapStats {
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(),
}
}

View file

@ -35,5 +35,8 @@ declare namespace Deno {
/** Close the resource with the specified op id. */ /** Close the resource with the specified op id. */
function close(rid: number): void; function close(rid: number): void;
/** Get heap stats for current isolate/worker */
function heapStats(): Record<string, number>;
} }
} }