mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 20:38:55 +00:00
feat(ext/node): perf_hooks.monitorEventLoopDelay() (#26905)
Fixes https://github.com/denoland/deno/issues/20961 Depends on https://github.com/denoland/deno_core/pull/965 and https://github.com/denoland/deno_core/pull/966
This commit is contained in:
parent
0e2f6e38e7
commit
069bc15030
44
Cargo.lock
generated
44
Cargo.lock
generated
@ -1452,9 +1452,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deno_core"
|
name = "deno_core"
|
||||||
version = "0.319.0"
|
version = "0.320.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e9dbb841f9850534320d8927dce53ca8d64bafbab5576c2a98f03f9e08534215"
|
checksum = "f285eed7b072749f9c3a9c4cf2c9ebb06462a2c22afec94892a6684c38f32696"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -1929,6 +1929,7 @@ dependencies = [
|
|||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-eld",
|
||||||
"url",
|
"url",
|
||||||
"webpki-root-certs",
|
"webpki-root-certs",
|
||||||
"winapi",
|
"winapi",
|
||||||
@ -1959,13 +1960,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deno_ops"
|
name = "deno_ops"
|
||||||
version = "0.195.0"
|
version = "0.196.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "797f348c38c07a5398bf790b280077c698e13fb49252f61ca6f6c5c616060292"
|
checksum = "d35c75ae05062f37ec2ae5fd1d99b2dcdfa0aef70844d3706759b8775056c5f6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-rules",
|
"proc-macro-rules",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
"stringcase",
|
||||||
"strum",
|
"strum",
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
"syn 2.0.87",
|
"syn 2.0.87",
|
||||||
@ -3560,6 +3562,20 @@ dependencies = [
|
|||||||
"hashbrown 0.14.5",
|
"hashbrown 0.14.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hdrhistogram"
|
||||||
|
version = "7.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.7",
|
||||||
|
"byteorder",
|
||||||
|
"crossbeam-channel",
|
||||||
|
"flate2",
|
||||||
|
"nom 7.1.3",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -6406,9 +6422,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_v8"
|
name = "serde_v8"
|
||||||
version = "0.228.0"
|
version = "0.229.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bfe23e75c9a167f4e9d67a90d9fcaa622d1eec9aecad526c270e99a92f6915ff"
|
checksum = "4e1dbbda82d67a393ea96f75d8383bc41fcd0bba43164aeaab599e1c2c2d46d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"serde",
|
"serde",
|
||||||
@ -6721,6 +6737,12 @@ dependencies = [
|
|||||||
"syn 2.0.87",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stringcase"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04028eeb851ed08af6aba5caa29f2d59a13ed168cee4d6bd753aeefcf1d636b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strip-ansi-escapes"
|
name = "strip-ansi-escapes"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -7522,6 +7544,16 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-eld"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9166030f05d6bc5642bdb8f8c2be31eb3c02cd465d662bcdc2df82d4aa41a584"
|
||||||
|
dependencies = [
|
||||||
|
"hdrhistogram",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
|
@ -46,7 +46,7 @@ repository = "https://github.com/denoland/deno"
|
|||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
deno_ast = { version = "=0.43.3", features = ["transpiling"] }
|
deno_ast = { version = "=0.43.3", features = ["transpiling"] }
|
||||||
deno_core = { version = "0.319.0" }
|
deno_core = { version = "0.320.0" }
|
||||||
|
|
||||||
deno_bench_util = { version = "0.171.0", path = "./bench_util" }
|
deno_bench_util = { version = "0.171.0", path = "./bench_util" }
|
||||||
deno_config = { version = "=0.39.1", features = ["workspace", "sync"] }
|
deno_config = { version = "=0.39.1", features = ["workspace", "sync"] }
|
||||||
|
@ -15,6 +15,7 @@ use dlopen2::raw::Library;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_value::ValueDeserializer;
|
use serde_value::ValueDeserializer;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -126,14 +127,17 @@ pub struct FfiLoadArgs {
|
|||||||
#[op2]
|
#[op2]
|
||||||
pub fn op_ffi_load<'scope, FP>(
|
pub fn op_ffi_load<'scope, FP>(
|
||||||
scope: &mut v8::HandleScope<'scope>,
|
scope: &mut v8::HandleScope<'scope>,
|
||||||
state: &mut OpState,
|
state: Rc<RefCell<OpState>>,
|
||||||
#[serde] args: FfiLoadArgs,
|
#[serde] args: FfiLoadArgs,
|
||||||
) -> Result<v8::Local<'scope, v8::Value>, DlfcnError>
|
) -> Result<v8::Local<'scope, v8::Value>, DlfcnError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
{
|
{
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let path = {
|
||||||
let path = permissions.check_partial_with_path(&args.path)?;
|
let mut state = state.borrow_mut();
|
||||||
|
let permissions = state.borrow_mut::<FP>();
|
||||||
|
permissions.check_partial_with_path(&args.path)?
|
||||||
|
};
|
||||||
|
|
||||||
let lib = Library::open(&path).map_err(|e| {
|
let lib = Library::open(&path).map_err(|e| {
|
||||||
dlopen2::Error::OpeningLibraryError(std::io::Error::new(
|
dlopen2::Error::OpeningLibraryError(std::io::Error::new(
|
||||||
@ -215,6 +219,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut state = state.borrow_mut();
|
||||||
let out = v8::Array::new(scope, 2);
|
let out = v8::Array::new(scope, 2);
|
||||||
let rid = state.resource_table.add(resource);
|
let rid = state.resource_table.add(resource);
|
||||||
let rid_v8 = v8::Integer::new_from_unsigned(scope, rid);
|
let rid_v8 = v8::Integer::new_from_unsigned(scope, rid);
|
||||||
|
@ -95,6 +95,7 @@ spki.workspace = true
|
|||||||
stable_deref_trait = "1.2.0"
|
stable_deref_trait = "1.2.0"
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
tokio-eld = "0.2"
|
||||||
url.workspace = true
|
url.workspace = true
|
||||||
webpki-root-certs.workspace = true
|
webpki-root-certs.workspace = true
|
||||||
winapi.workspace = true
|
winapi.workspace = true
|
||||||
|
@ -427,6 +427,9 @@ deno_core::extension!(deno_node,
|
|||||||
ops::inspector::op_inspector_emit_protocol_event,
|
ops::inspector::op_inspector_emit_protocol_event,
|
||||||
ops::inspector::op_inspector_enabled,
|
ops::inspector::op_inspector_enabled,
|
||||||
],
|
],
|
||||||
|
objects = [
|
||||||
|
ops::perf_hooks::EldHistogram
|
||||||
|
],
|
||||||
esm_entry_point = "ext:deno_node/02_init.js",
|
esm_entry_point = "ext:deno_node/02_init.js",
|
||||||
esm = [
|
esm = [
|
||||||
dir "polyfills",
|
dir "polyfills",
|
||||||
|
@ -10,6 +10,7 @@ pub mod idna;
|
|||||||
pub mod inspector;
|
pub mod inspector;
|
||||||
pub mod ipc;
|
pub mod ipc;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
|
pub mod perf_hooks;
|
||||||
pub mod process;
|
pub mod process;
|
||||||
pub mod require;
|
pub mod require;
|
||||||
pub mod tls;
|
pub mod tls;
|
||||||
|
135
ext/node/ops/perf_hooks.rs
Normal file
135
ext/node/ops/perf_hooks.rs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use deno_core::op2;
|
||||||
|
use deno_core::GarbageCollected;
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum PerfHooksError {
|
||||||
|
#[error(transparent)]
|
||||||
|
TokioEld(#[from] tokio_eld::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EldHistogram {
|
||||||
|
eld: tokio_eld::EldHistogram<u64>,
|
||||||
|
started: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GarbageCollected for EldHistogram {}
|
||||||
|
|
||||||
|
#[op2]
|
||||||
|
impl EldHistogram {
|
||||||
|
// Creates an interval EldHistogram object that samples and reports the event
|
||||||
|
// loop delay over time.
|
||||||
|
//
|
||||||
|
// The delays will be reported in nanoseconds.
|
||||||
|
#[constructor]
|
||||||
|
#[cppgc]
|
||||||
|
pub fn new(#[smi] resolution: u32) -> Result<EldHistogram, PerfHooksError> {
|
||||||
|
Ok(EldHistogram {
|
||||||
|
eld: tokio_eld::EldHistogram::new(resolution as usize)?,
|
||||||
|
started: Cell::new(false),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disables the update interval timer.
|
||||||
|
//
|
||||||
|
// Returns true if the timer was stopped, false if it was already stopped.
|
||||||
|
#[fast]
|
||||||
|
fn enable(&self) -> bool {
|
||||||
|
if self.started.get() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.eld.start();
|
||||||
|
self.started.set(true);
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enables the update interval timer.
|
||||||
|
//
|
||||||
|
// Returns true if the timer was started, false if it was already started.
|
||||||
|
#[fast]
|
||||||
|
fn disable(&self) -> bool {
|
||||||
|
if !self.started.get() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.eld.stop();
|
||||||
|
self.started.set(false);
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the value at the given percentile.
|
||||||
|
//
|
||||||
|
// `percentile` ∈ (0, 100]
|
||||||
|
#[fast]
|
||||||
|
#[number]
|
||||||
|
fn percentile(&self, percentile: f64) -> u64 {
|
||||||
|
self.eld.value_at_percentile(percentile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the value at the given percentile as a bigint.
|
||||||
|
#[fast]
|
||||||
|
#[bigint]
|
||||||
|
fn percentile_big_int(&self, percentile: f64) -> u64 {
|
||||||
|
self.eld.value_at_percentile(percentile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number of samples recorded by the histogram.
|
||||||
|
#[getter]
|
||||||
|
#[number]
|
||||||
|
fn count(&self) -> u64 {
|
||||||
|
self.eld.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number of samples recorded by the histogram as a bigint.
|
||||||
|
#[getter]
|
||||||
|
#[bigint]
|
||||||
|
fn count_big_int(&self) -> u64 {
|
||||||
|
self.eld.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The maximum recorded event loop delay.
|
||||||
|
#[getter]
|
||||||
|
#[number]
|
||||||
|
fn max(&self) -> u64 {
|
||||||
|
self.eld.max()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The maximum recorded event loop delay as a bigint.
|
||||||
|
#[getter]
|
||||||
|
#[bigint]
|
||||||
|
fn max_big_int(&self) -> u64 {
|
||||||
|
self.eld.max()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The mean of the recorded event loop delays.
|
||||||
|
#[getter]
|
||||||
|
fn mean(&self) -> f64 {
|
||||||
|
self.eld.mean()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The minimum recorded event loop delay.
|
||||||
|
#[getter]
|
||||||
|
#[number]
|
||||||
|
fn min(&self) -> u64 {
|
||||||
|
self.eld.min()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The minimum recorded event loop delay as a bigint.
|
||||||
|
#[getter]
|
||||||
|
#[bigint]
|
||||||
|
fn min_big_int(&self) -> u64 {
|
||||||
|
self.eld.min()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The standard deviation of the recorded event loop delays.
|
||||||
|
#[getter]
|
||||||
|
fn stddev(&self) -> f64 {
|
||||||
|
self.eld.stdev()
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import {
|
|||||||
performance as shimPerformance,
|
performance as shimPerformance,
|
||||||
PerformanceEntry,
|
PerformanceEntry,
|
||||||
} from "ext:deno_web/15_performance.js";
|
} from "ext:deno_web/15_performance.js";
|
||||||
|
import { EldHistogram } from "ext:core/ops";
|
||||||
|
|
||||||
class PerformanceObserver {
|
class PerformanceObserver {
|
||||||
static supportedEntryTypes: string[] = [];
|
static supportedEntryTypes: string[] = [];
|
||||||
@ -89,10 +90,11 @@ const performance:
|
|||||||
) => shimPerformance.dispatchEvent(...args),
|
) => shimPerformance.dispatchEvent(...args),
|
||||||
};
|
};
|
||||||
|
|
||||||
const monitorEventLoopDelay = () =>
|
function monitorEventLoopDelay(options = {}) {
|
||||||
notImplemented(
|
const { resolution = 10 } = options;
|
||||||
"monitorEventLoopDelay from performance",
|
|
||||||
);
|
return new EldHistogram(resolution);
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
performance,
|
performance,
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
performance,
|
performance,
|
||||||
PerformanceObserver,
|
PerformanceObserver,
|
||||||
} from "node:perf_hooks";
|
} from "node:perf_hooks";
|
||||||
import { assertEquals, assertThrows } from "@std/assert";
|
import { assert, assertEquals, assertThrows } from "@std/assert";
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "[perf_hooks] performance",
|
name: "[perf_hooks] performance",
|
||||||
@ -73,11 +73,16 @@ Deno.test("[perf_hooks]: eventLoopUtilization", () => {
|
|||||||
assertEquals(typeof obj.utilization, "number");
|
assertEquals(typeof obj.utilization, "number");
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("[perf_hooks]: monitorEventLoopDelay", () => {
|
Deno.test("[perf_hooks]: monitorEventLoopDelay", async () => {
|
||||||
const e = assertThrows(() => {
|
const e = monitorEventLoopDelay();
|
||||||
monitorEventLoopDelay({ resolution: 1 });
|
assertEquals(e.count, 0);
|
||||||
});
|
e.enable();
|
||||||
|
|
||||||
// deno-lint-ignore no-explicit-any
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
assertEquals((e as any).code, "ERR_NOT_IMPLEMENTED");
|
|
||||||
|
assert(e.min > 0);
|
||||||
|
assert(e.minBigInt > 0n);
|
||||||
|
assert(e.count > 0);
|
||||||
|
|
||||||
|
e.disable();
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user