diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 1632d03bec..a9353e2ff1 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -850,13 +850,13 @@ declare namespace Deno { | "TXT"; export interface ResolveDnsOptions { - /** The name server to be used for lookups. - * If not specified, defaults to the system configuration e.g. `/etc/resolv.conf` on Unix. */ + /** The name server to be used for lookups. + * If not specified, defaults to the system configuration e.g. `/etc/resolv.conf` on Unix. */ nameServer?: { /** The IP address of the name server */ ipAddr: string; /** The port number the query will be sent to. - * If not specified, defaults to 53. */ + * If not specified, defaults to 53. */ port?: number; }; } @@ -1343,6 +1343,24 @@ declare namespace Deno { * ``` */ export function sleepSync(millis: number): Promise; + + export interface Metrics extends OpMetrics { + ops: Record; + } + + export interface OpMetrics { + opsDispatched: number; + opsDispatchedSync: number; + opsDispatchedAsync: number; + opsDispatchedAsyncUnref: number; + opsCompleted: number; + opsCompletedSync: number; + opsCompletedAsync: number; + opsCompletedAsyncUnref: number; + bytesSentControl: number; + bytesSentData: number; + bytesReceived: number; + } } declare function fetch( diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs index c46c5cd200..e477221984 100644 --- a/cli/ops/mod.rs +++ b/cli/ops/mod.rs @@ -21,7 +21,7 @@ where F: Fn(Rc>, Value, BufVec) -> R + 'static, R: Future> + 'static, { - rt.register_op(name, metrics_op(json_op_async(op_fn))); + rt.register_op(name, metrics_op(name, json_op_async(op_fn))); } pub fn reg_json_sync(rt: &mut JsRuntime, name: &'static str, op_fn: F) @@ -29,5 +29,5 @@ where F: Fn(&mut OpState, Value, &mut [ZeroCopyBuf]) -> Result + 'static, { - rt.register_op(name, metrics_op(json_op_sync(op_fn))); + rt.register_op(name, metrics_op(name, json_op_sync(op_fn))); } diff --git a/cli/tests/unit/metrics_test.ts b/cli/tests/unit/metrics_test.ts index a60aae427d..2f12ac90d1 100644 --- a/cli/tests/unit/metrics_test.ts +++ b/cli/tests/unit/metrics_test.ts @@ -2,30 +2,40 @@ import { assert, unitTest } from "./test_util.ts"; unitTest(async function metrics(): Promise { - const m1 = Deno.metrics(); - assert(m1.opsDispatched > 0); - assert(m1.opsDispatchedSync > 0); - assert(m1.opsCompleted > 0); - assert(m1.opsCompletedSync > 0); - assert(m1.bytesSentControl > 0); - assert(m1.bytesSentData >= 0); - assert(m1.bytesReceived > 0); - // Write to stdout to ensure a "data" message gets sent instead of just // control messages. const dataMsg = new Uint8Array([13, 13, 13]); // "\r\r\r", await Deno.stdout.write(dataMsg); + const m1 = Deno.metrics(); + assert(m1.opsDispatched > 0); + assert(m1.opsCompleted > 0); + assert(m1.bytesSentControl > 0); + assert(m1.bytesSentData >= 0); + assert(m1.bytesReceived > 0); + const m1OpWrite = m1.ops["op_write"]; + assert(m1OpWrite.opsDispatchedAsync > 0); + assert(m1OpWrite.opsCompletedAsync > 0); + assert(m1OpWrite.bytesSentControl > 0); + assert(m1OpWrite.bytesSentData >= 0); + assert(m1OpWrite.bytesReceived > 0); + + await Deno.stdout.write(dataMsg); + const m2 = Deno.metrics(); - assert(m2.opsDispatched > m1.opsDispatched); - assert(m2.opsDispatchedSync > m1.opsDispatchedSync); assert(m2.opsDispatchedAsync > m1.opsDispatchedAsync); - assert(m2.opsCompleted > m1.opsCompleted); - assert(m2.opsCompletedSync > m1.opsCompletedSync); assert(m2.opsCompletedAsync > m1.opsCompletedAsync); assert(m2.bytesSentControl > m1.bytesSentControl); assert(m2.bytesSentData >= m1.bytesSentData + dataMsg.byteLength); assert(m2.bytesReceived > m1.bytesReceived); + const m2OpWrite = m2.ops["op_write"]; + assert(m2OpWrite.opsDispatchedAsync > m1OpWrite.opsDispatchedAsync); + assert(m2OpWrite.opsCompletedAsync > m1OpWrite.opsCompletedAsync); + assert(m2OpWrite.bytesSentControl > m1OpWrite.bytesSentControl); + assert( + m2OpWrite.bytesSentData >= m1OpWrite.bytesSentData + dataMsg.byteLength, + ); + assert(m2OpWrite.bytesReceived > m1OpWrite.bytesReceived); }); unitTest( diff --git a/runtime/js/30_metrics.js b/runtime/js/30_metrics.js index 30fa7cf80b..ed062fce38 100644 --- a/runtime/js/30_metrics.js +++ b/runtime/js/30_metrics.js @@ -5,7 +5,11 @@ const core = window.Deno.core; function metrics() { - return core.jsonOpSync("op_metrics"); + const { combined, ops } = core.jsonOpSync("op_metrics"); + if (ops) { + combined.ops = ops; + } + return combined; } window.__bootstrap.metrics = { diff --git a/runtime/metrics.rs b/runtime/metrics.rs index b609f24666..58924f0cbe 100644 --- a/runtime/metrics.rs +++ b/runtime/metrics.rs @@ -1,6 +1,36 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +use serde::Serialize; + #[derive(Default, Debug)] -pub struct Metrics { +pub struct RuntimeMetrics { + pub ops: HashMap<&'static str, OpMetrics>, +} + +impl RuntimeMetrics { + pub fn combined_metrics(&self) -> OpMetrics { + let mut total = OpMetrics::default(); + + for metrics in self.ops.values() { + total.ops_dispatched += metrics.ops_dispatched; + total.ops_dispatched_sync += metrics.ops_dispatched_sync; + total.ops_dispatched_async += metrics.ops_dispatched_async; + total.ops_dispatched_async_unref += metrics.ops_dispatched_async_unref; + total.ops_completed += metrics.ops_completed; + total.ops_completed_sync += metrics.ops_completed_sync; + total.ops_completed_async += metrics.ops_completed_async; + total.ops_completed_async_unref += metrics.ops_completed_async_unref; + total.bytes_sent_control += metrics.bytes_sent_control; + total.bytes_sent_data += metrics.bytes_sent_data; + total.bytes_received += metrics.bytes_received; + } + + total + } +} + +#[derive(Default, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OpMetrics { pub ops_dispatched: u64, pub ops_dispatched_sync: u64, pub ops_dispatched_async: u64, @@ -14,7 +44,7 @@ pub struct Metrics { pub bytes_received: u64, } -impl Metrics { +impl OpMetrics { fn op_dispatched( &mut self, bytes_sent_control: usize, @@ -76,9 +106,10 @@ use deno_core::Op; use deno_core::OpFn; use deno_core::OpState; use std::cell::RefCell; +use std::collections::HashMap; use std::rc::Rc; -pub fn metrics_op(op_fn: Box) -> Box { +pub fn metrics_op(name: &'static str, op_fn: Box) -> Box { Box::new(move |op_state: Rc>, bufs: BufVec| -> Op { // TODOs: // * The 'bytes' metrics seem pretty useless, especially now that the @@ -94,7 +125,14 @@ pub fn metrics_op(op_fn: Box) -> Box { let op_state_ = op_state.clone(); let mut s = op_state.borrow_mut(); - let metrics = s.borrow_mut::(); + let runtime_metrics = s.borrow_mut::(); + + let metrics = if let Some(metrics) = runtime_metrics.ops.get_mut(name) { + metrics + } else { + runtime_metrics.ops.insert(name, OpMetrics::default()); + runtime_metrics.ops.get_mut(name).unwrap() + }; use deno_core::futures::future::FutureExt; @@ -108,7 +146,8 @@ pub fn metrics_op(op_fn: Box) -> Box { let fut = fut .inspect(move |buf| { let mut s = op_state_.borrow_mut(); - let metrics = s.borrow_mut::(); + let runtime_metrics = s.borrow_mut::(); + let metrics = runtime_metrics.ops.get_mut(name).unwrap(); metrics.op_completed_async(buf.len()); }) .boxed_local(); @@ -119,7 +158,8 @@ pub fn metrics_op(op_fn: Box) -> Box { let fut = fut .inspect(move |buf| { let mut s = op_state_.borrow_mut(); - let metrics = s.borrow_mut::(); + let runtime_metrics = s.borrow_mut::(); + let metrics = runtime_metrics.ops.get_mut(name).unwrap(); metrics.op_completed_async_unref(buf.len()); }) .boxed_local(); diff --git a/runtime/ops/io.rs b/runtime/ops/io.rs index 2ac8e1b78e..bda8a51cb9 100644 --- a/runtime/ops/io.rs +++ b/runtime/ops/io.rs @@ -105,8 +105,8 @@ lazy_static! { } pub fn init(rt: &mut JsRuntime) { - rt.register_op("op_read", metrics_op(minimal_op(op_read))); - rt.register_op("op_write", metrics_op(minimal_op(op_write))); + rt.register_op("op_read", metrics_op("op_read", minimal_op(op_read))); + rt.register_op("op_write", metrics_op("op_write", minimal_op(op_write))); super::reg_json_async(rt, "op_shutdown", op_shutdown); } diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs index 3ead7efa2e..e5dba723a8 100644 --- a/runtime/ops/mod.rs +++ b/runtime/ops/mod.rs @@ -48,7 +48,7 @@ pub fn reg_json_async( R: Future> + 'static, RV: Serialize, { - rt.register_op(name, metrics_op(json_op_async(op_fn))); + rt.register_op(name, metrics_op(name, json_op_async(op_fn))); } pub fn reg_json_sync(rt: &mut JsRuntime, name: &'static str, op_fn: F) @@ -57,7 +57,7 @@ where V: DeserializeOwned, R: Serialize, { - rt.register_op(name, metrics_op(json_op_sync(op_fn))); + rt.register_op(name, metrics_op(name, json_op_sync(op_fn))); } /// `UnstableChecker` is a struct so it can be placed inside `GothamState`; diff --git a/runtime/ops/plugin.rs b/runtime/ops/plugin.rs index e972df0463..424b1dca03 100644 --- a/runtime/ops/plugin.rs +++ b/runtime/ops/plugin.rs @@ -126,10 +126,13 @@ impl<'a> plugin_api::Interface for PluginInterface<'a> { _ => unreachable!(), } }; - self - .state - .op_table - .register_op(name, metrics_op(Box::new(plugin_op_fn))) + self.state.op_table.register_op( + name, + metrics_op( + Box::leak(Box::new(name.to_string())), + Box::new(plugin_op_fn), + ), + ) } } diff --git a/runtime/ops/runtime.rs b/runtime/ops/runtime.rs index 77abc45b72..a2f377bed5 100644 --- a/runtime/ops/runtime.rs +++ b/runtime/ops/runtime.rs @@ -1,6 +1,7 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -use crate::metrics::Metrics; +use crate::metrics::RuntimeMetrics; +use crate::ops::UnstableChecker; use crate::permissions::Permissions; use deno_core::error::AnyError; use deno_core::serde_json; @@ -42,21 +43,15 @@ fn op_metrics( _args: Value, _zero_copy: &mut [ZeroCopyBuf], ) -> Result { - let m = state.borrow::(); - - Ok(json!({ - "opsDispatched": m.ops_dispatched, - "opsDispatchedSync": m.ops_dispatched_sync, - "opsDispatchedAsync": m.ops_dispatched_async, - "opsDispatchedAsyncUnref": m.ops_dispatched_async_unref, - "opsCompleted": m.ops_completed, - "opsCompletedSync": m.ops_completed_sync, - "opsCompletedAsync": m.ops_completed_async, - "opsCompletedAsyncUnref": m.ops_completed_async_unref, - "bytesSentControl": m.bytes_sent_control, - "bytesSentData": m.bytes_sent_data, - "bytesReceived": m.bytes_received - })) + let m = state.borrow::(); + let combined = m.combined_metrics(); + let unstable_checker = state.borrow::(); + let maybe_ops = if unstable_checker.unstable { + Some(&m.ops) + } else { + None + }; + Ok(json!({ "combined": combined, "ops": maybe_ops })) } pub fn ppid() -> Value { diff --git a/runtime/ops/timers.rs b/runtime/ops/timers.rs index a00b04ed09..783395cada 100644 --- a/runtime/ops/timers.rs +++ b/runtime/ops/timers.rs @@ -81,7 +81,7 @@ pub fn init(rt: &mut deno_core::JsRuntime) { super::reg_json_sync(rt, "op_global_timer_stop", op_global_timer_stop); super::reg_json_sync(rt, "op_global_timer_start", op_global_timer_start); super::reg_json_async(rt, "op_global_timer", op_global_timer); - rt.register_op("op_now", metrics_op(minimal_op(op_now))); + rt.register_op("op_now", metrics_op("op_now", minimal_op(op_now))); super::reg_json_sync(rt, "op_sleep_sync", op_sleep_sync); } diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 0847059acc..082cf7267a 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -4,7 +4,7 @@ use crate::colors; use crate::inspector::DenoInspector; use crate::inspector::InspectorServer; use crate::js; -use crate::metrics::Metrics; +use crate::metrics::RuntimeMetrics; use crate::ops; use crate::permissions::Permissions; use crate::tokio_util::create_basic_runtime; @@ -209,9 +209,9 @@ impl WebWorker { { let op_state = js_runtime.op_state(); let mut op_state = op_state.borrow_mut(); - op_state.put::(Default::default()); + op_state.put(RuntimeMetrics::default()); op_state.put::(permissions); - op_state.put::(ops::UnstableChecker { + op_state.put(ops::UnstableChecker { unstable: options.unstable, }); } diff --git a/runtime/worker.rs b/runtime/worker.rs index dca01932db..0b3fe3e10f 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -4,7 +4,7 @@ use crate::inspector::DenoInspector; use crate::inspector::InspectorServer; use crate::inspector::InspectorSession; use crate::js; -use crate::metrics::Metrics; +use crate::metrics::RuntimeMetrics; use crate::ops; use crate::permissions::Permissions; use deno_core::error::AnyError; @@ -104,9 +104,9 @@ impl MainWorker { { let op_state = js_runtime.op_state(); let mut op_state = op_state.borrow_mut(); - op_state.put::(Default::default()); + op_state.put(RuntimeMetrics::default()); op_state.put::(permissions); - op_state.put::(ops::UnstableChecker { + op_state.put(ops::UnstableChecker { unstable: options.unstable, }); }