summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2024-11-19 16:49:25 +0530
committerGitHub <noreply@github.com>2024-11-19 16:49:25 +0530
commit069bc15030225393f7d05521505316066464bdbd (patch)
tree8665758ced09dc2876aeedadffe27d2f0e181a09
parent0e2f6e38e7b93e42099f546ef2c01629964d095a (diff)
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
-rw-r--r--Cargo.lock44
-rw-r--r--Cargo.toml2
-rw-r--r--ext/ffi/dlfcn.rs11
-rw-r--r--ext/node/Cargo.toml1
-rw-r--r--ext/node/lib.rs3
-rw-r--r--ext/node/ops/mod.rs1
-rw-r--r--ext/node/ops/perf_hooks.rs135
-rw-r--r--ext/node/polyfills/perf_hooks.ts10
-rw-r--r--tests/unit_node/perf_hooks_test.ts19
9 files changed, 205 insertions, 21 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 56d241741..13495855c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1452,9 +1452,9 @@ dependencies = [
[[package]]
name = "deno_core"
-version = "0.319.0"
+version = "0.320.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9dbb841f9850534320d8927dce53ca8d64bafbab5576c2a98f03f9e08534215"
+checksum = "f285eed7b072749f9c3a9c4cf2c9ebb06462a2c22afec94892a6684c38f32696"
dependencies = [
"anyhow",
"bincode",
@@ -1929,6 +1929,7 @@ dependencies = [
"stable_deref_trait",
"thiserror",
"tokio",
+ "tokio-eld",
"url",
"webpki-root-certs",
"winapi",
@@ -1959,13 +1960,14 @@ dependencies = [
[[package]]
name = "deno_ops"
-version = "0.195.0"
+version = "0.196.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "797f348c38c07a5398bf790b280077c698e13fb49252f61ca6f6c5c616060292"
+checksum = "d35c75ae05062f37ec2ae5fd1d99b2dcdfa0aef70844d3706759b8775056c5f6"
dependencies = [
"proc-macro-rules",
"proc-macro2",
"quote",
+ "stringcase",
"strum",
"strum_macros",
"syn 2.0.87",
@@ -3561,6 +3563,20 @@ dependencies = [
]
[[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]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -6406,9 +6422,9 @@ dependencies = [
[[package]]
name = "serde_v8"
-version = "0.228.0"
+version = "0.229.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfe23e75c9a167f4e9d67a90d9fcaa622d1eec9aecad526c270e99a92f6915ff"
+checksum = "4e1dbbda82d67a393ea96f75d8383bc41fcd0bba43164aeaab599e1c2c2d46d7"
dependencies = [
"num-bigint",
"serde",
@@ -6722,6 +6738,12 @@ dependencies = [
]
[[package]]
+name = "stringcase"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04028eeb851ed08af6aba5caa29f2d59a13ed168cee4d6bd753aeefcf1d636b0"
+
+[[package]]
name = "strip-ansi-escapes"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -7523,6 +7545,16 @@ dependencies = [
]
[[package]]
+name = "tokio-eld"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9166030f05d6bc5642bdb8f8c2be31eb3c02cd465d662bcdc2df82d4aa41a584"
+dependencies = [
+ "hdrhistogram",
+ "tokio",
+]
+
+[[package]]
name = "tokio-macros"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 9d625f082..cfe67ba69 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -46,7 +46,7 @@ repository = "https://github.com/denoland/deno"
[workspace.dependencies]
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_config = { version = "=0.39.1", features = ["workspace", "sync"] }
diff --git a/ext/ffi/dlfcn.rs b/ext/ffi/dlfcn.rs
index 55909468f..26d1b71e9 100644
--- a/ext/ffi/dlfcn.rs
+++ b/ext/ffi/dlfcn.rs
@@ -15,6 +15,7 @@ use dlopen2::raw::Library;
use serde::Deserialize;
use serde_value::ValueDeserializer;
use std::borrow::Cow;
+use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::c_void;
use std::rc::Rc;
@@ -126,14 +127,17 @@ pub struct FfiLoadArgs {
#[op2]
pub fn op_ffi_load<'scope, FP>(
scope: &mut v8::HandleScope<'scope>,
- state: &mut OpState,
+ state: Rc<RefCell<OpState>>,
#[serde] args: FfiLoadArgs,
) -> Result<v8::Local<'scope, v8::Value>, DlfcnError>
where
FP: FfiPermissions + 'static,
{
- let permissions = state.borrow_mut::<FP>();
- let path = permissions.check_partial_with_path(&args.path)?;
+ let 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| {
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 rid = state.resource_table.add(resource);
let rid_v8 = v8::Integer::new_from_unsigned(scope, rid);
diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml
index c4ee86a95..36910a844 100644
--- a/ext/node/Cargo.toml
+++ b/ext/node/Cargo.toml
@@ -95,6 +95,7 @@ spki.workspace = true
stable_deref_trait = "1.2.0"
thiserror.workspace = true
tokio.workspace = true
+tokio-eld = "0.2"
url.workspace = true
webpki-root-certs.workspace = true
winapi.workspace = true
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 84f39552c..63f5794b7 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -427,6 +427,9 @@ deno_core::extension!(deno_node,
ops::inspector::op_inspector_emit_protocol_event,
ops::inspector::op_inspector_enabled,
],
+ objects = [
+ ops::perf_hooks::EldHistogram
+ ],
esm_entry_point = "ext:deno_node/02_init.js",
esm = [
dir "polyfills",
diff --git a/ext/node/ops/mod.rs b/ext/node/ops/mod.rs
index b53f19dc2..e5ea8b417 100644
--- a/ext/node/ops/mod.rs
+++ b/ext/node/ops/mod.rs
@@ -10,6 +10,7 @@ pub mod idna;
pub mod inspector;
pub mod ipc;
pub mod os;
+pub mod perf_hooks;
pub mod process;
pub mod require;
pub mod tls;
diff --git a/ext/node/ops/perf_hooks.rs b/ext/node/ops/perf_hooks.rs
new file mode 100644
index 000000000..636d0b2ad
--- /dev/null
+++ b/ext/node/ops/perf_hooks.rs
@@ -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()
+ }
+}
diff --git a/ext/node/polyfills/perf_hooks.ts b/ext/node/polyfills/perf_hooks.ts
index d92b925b5..ec76b3ce2 100644
--- a/ext/node/polyfills/perf_hooks.ts
+++ b/ext/node/polyfills/perf_hooks.ts
@@ -8,6 +8,7 @@ import {
performance as shimPerformance,
PerformanceEntry,
} from "ext:deno_web/15_performance.js";
+import { EldHistogram } from "ext:core/ops";
class PerformanceObserver {
static supportedEntryTypes: string[] = [];
@@ -89,10 +90,11 @@ const performance:
) => shimPerformance.dispatchEvent(...args),
};
-const monitorEventLoopDelay = () =>
- notImplemented(
- "monitorEventLoopDelay from performance",
- );
+function monitorEventLoopDelay(options = {}) {
+ const { resolution = 10 } = options;
+
+ return new EldHistogram(resolution);
+}
export default {
performance,
diff --git a/tests/unit_node/perf_hooks_test.ts b/tests/unit_node/perf_hooks_test.ts
index 8247f9fd3..83d006222 100644
--- a/tests/unit_node/perf_hooks_test.ts
+++ b/tests/unit_node/perf_hooks_test.ts
@@ -5,7 +5,7 @@ import {
performance,
PerformanceObserver,
} from "node:perf_hooks";
-import { assertEquals, assertThrows } from "@std/assert";
+import { assert, assertEquals, assertThrows } from "@std/assert";
Deno.test({
name: "[perf_hooks] performance",
@@ -73,11 +73,16 @@ Deno.test("[perf_hooks]: eventLoopUtilization", () => {
assertEquals(typeof obj.utilization, "number");
});
-Deno.test("[perf_hooks]: monitorEventLoopDelay", () => {
- const e = assertThrows(() => {
- monitorEventLoopDelay({ resolution: 1 });
- });
+Deno.test("[perf_hooks]: monitorEventLoopDelay", async () => {
+ const e = monitorEventLoopDelay();
+ assertEquals(e.count, 0);
+ e.enable();
- // deno-lint-ignore no-explicit-any
- assertEquals((e as any).code, "ERR_NOT_IMPLEMENTED");
+ await new Promise((resolve) => setTimeout(resolve, 100));
+
+ assert(e.min > 0);
+ assert(e.minBigInt > 0n);
+ assert(e.count > 0);
+
+ e.disable();
});