Skip to content

Commit

Permalink
feat(ext/node): perf_hooks.monitorEventLoopDelay() (#26905)
Browse files Browse the repository at this point in the history
  • Loading branch information
littledivy authored Nov 19, 2024
1 parent 0e2f6e3 commit 069bc15
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 21 deletions.
44 changes: 38 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
11 changes: 8 additions & 3 deletions ext/ffi/dlfcn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions ext/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions ext/node/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions ext/node/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
135 changes: 135 additions & 0 deletions ext/node/ops/perf_hooks.rs
Original file line number Diff line number Diff line change
@@ -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()
}
}
10 changes: 6 additions & 4 deletions ext/node/polyfills/perf_hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = [];
Expand Down Expand Up @@ -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,
Expand Down
19 changes: 12 additions & 7 deletions tests/unit_node/perf_hooks_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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();
});

0 comments on commit 069bc15

Please sign in to comment.