Repository: denoland/deno_core
Pull request #492 (2024-01-26 01:58:48)

feat(core): more enhancements to the stats API
Add an op_exclusions filter and keep op names as `&'static str`.


Patch:
diff --git a/core/runtime/op_driver/futures_unordered_driver.rs b/core/runtime/op_driver/futures_unordered_driver.rs
index 3b7601b..a85ed56 100644
--- a/core/runtime/op_driver/futures_unordered_driver.rs
+++ b/core/runtime/op_driver/futures_unordered_driver.rs
@@ -7,6 +7,7 @@ use super::OpInflightStats;
 use crate::OpId;
 use crate::PromiseId;
 use anyhow::Error;
+use bit_set::BitSet;
 use deno_unsync::spawn;
 use deno_unsync::JoinHandle;
 use deno_unsync::UnsyncWaker;
@@ -229,11 +230,14 @@ impl<C: OpMappingContext> OpDriver<C> for FuturesUnorderedDriver<C> {
     self.len.get()
   }
 
-  fn stats(&self) -> OpInflightStats {
+  fn stats(&self, op_exclusions: &BitSet) -> OpInflightStats {
     let q = self.queue.queue.queue.borrow();
     let mut v: Vec<PendingOpInfo> = Vec::with_capacity(self.len.get());
     for f in q.iter() {
-      v.push(f.context())
+      let context = f.context();
+      if !op_exclusions.contains(context.1 as _) {
+        v.push(context);
+      }
     }
     OpInflightStats {
       ops: v.into_boxed_slice(),
diff --git a/core/runtime/op_driver/joinset_driver.rs b/core/runtime/op_driver/joinset_driver.rs
index 171127d..93ba335 100644
--- a/core/runtime/op_driver/joinset_driver.rs
+++ b/core/runtime/op_driver/joinset_driver.rs
@@ -5,6 +5,7 @@ use super::OpInflightStats;
 use crate::OpId;
 use crate::PromiseId;
 use anyhow::Error;
+use bit_set::BitSet;
 use deno_unsync::JoinSet;
 use futures::task::noop_waker_ref;
 use futures::FutureExt;
@@ -155,7 +156,7 @@ impl<C: OpMappingContext> OpDriver<C> for JoinSetDriver<C> {
     self.pending_ops.borrow().len()
   }
 
-  fn stats(&self) -> OpInflightStats {
+  fn stats(&self, _op_exclusions: &BitSet) -> OpInflightStats {
     OpInflightStats::default()
   }
 }
diff --git a/core/runtime/op_driver/mod.rs b/core/runtime/op_driver/mod.rs
index 5422d9d..f20b730 100644
--- a/core/runtime/op_driver/mod.rs
+++ b/core/runtime/op_driver/mod.rs
@@ -2,6 +2,7 @@
 use crate::OpId;
 use crate::PromiseId;
 use anyhow::Error;
+use bit_set::BitSet;
 use std::future::Future;
 use std::task::Context;
 use std::task::Poll;
@@ -134,8 +135,10 @@ pub(crate) trait OpDriver<C: OpMappingContext = V8OpMappingContext>:
   /// may not be a cheap operation and calling it large number of times (for example, in an
   /// event loop) may cause slowdowns.
   ///
+  /// If `op_exclusions` is passed to this function, any ops in the set are excluded from the stats.
+  ///
   /// A [`PromiseId`] will appear in this list until its results have been picked up in `poll_ready`.
-  fn stats(&self) -> OpInflightStats;
+  fn stats(&self, op_exclusions: &BitSet) -> OpInflightStats;
 }
 
 #[cfg(test)]
diff --git a/core/runtime/stats.rs b/core/runtime/stats.rs
index 8bee3b3..6670026 100644
--- a/core/runtime/stats.rs
+++ b/core/runtime/stats.rs
@@ -2,6 +2,7 @@
 use super::op_driver::OpDriver;
 use super::op_driver::OpInflightStats;
 use super::ContextState;
+use crate::OpId;
 use crate::OpState;
 use crate::PromiseId;
 use crate::ResourceId;
@@ -17,11 +18,12 @@ pub struct RuntimeActivityStatsFactory {
 }
 
 /// Selects the statistics that you are interested in capturing.
-#[derive(Clone, Copy, Default, PartialEq, Eq)]
+#[derive(Clone, Default, PartialEq, Eq)]
 pub struct RuntimeActivityStatsFilter {
   include_timers: bool,
   include_ops: bool,
   include_resources: bool,
+  op_filter: BitSet,
 }
 
 impl RuntimeActivityStatsFilter {
@@ -30,6 +32,7 @@ impl RuntimeActivityStatsFilter {
       include_ops: true,
       include_resources: true,
       include_timers: true,
+      op_filter: BitSet::default(),
     }
   }
 
@@ -48,15 +51,28 @@ impl RuntimeActivityStatsFilter {
     self
   }
 
+  pub fn omit_op(mut self, op: OpId) -> Self {
+    self.op_filter.insert(op as _);
+    self
+  }
+
   pub fn is_empty(&self) -> bool {
-    *self == Self::default()
+    // This ensures we don't miss a newly-added field in the empty comparison
+    let Self {
+      include_ops,
+      include_resources,
+      include_timers,
+      op_filter: _,
+    } = self;
+    !(*include_ops) && !(*include_resources) && !(*include_timers)
   }
 }
 
 impl RuntimeActivityStatsFactory {
+  /// Capture the current runtime activity.
   pub fn capture(
     self,
-    filter: RuntimeActivityStatsFilter,
+    filter: &RuntimeActivityStatsFilter,
   ) -> RuntimeActivityStats {
     let resources = if filter.include_resources {
       let res = &self.op_state.borrow().resource_table;
@@ -91,7 +107,7 @@ impl RuntimeActivityStatsFactory {
     };
 
     let ops = if filter.include_ops {
-      self.context_state.pending_ops.stats()
+      self.context_state.pending_ops.stats(&filter.op_filter)
     } else {
       OpInflightStats::default()
     };
@@ -131,7 +147,7 @@ pub struct RuntimeActivityStats {
 #[derive(Debug, Serialize)]
 pub enum RuntimeActivity {
   /// An async op, including the promise ID and op name.
-  AsyncOp(PromiseId, String),
+  AsyncOp(PromiseId, &'static str),
   /// A resource, including the resource ID and name.
   Resource(ResourceId, String),
   /// A timer, including the timer ID.
@@ -173,10 +189,7 @@ impl RuntimeActivityStats {
     );
     let ops = self.context_state.op_ctxs.borrow();
     for op in self.ops.ops.iter() {
-      v.push(RuntimeActivity::AsyncOp(
-        op.0,
-        ops[op.1 as usize].decl.name.to_owned(),
-      ));
+      v.push(RuntimeActivity::AsyncOp(op.0, ops[op.1 as usize].decl.name));
     }
     for resource in self.resources.resources.iter() {
       v.push(RuntimeActivity::Resource(resource.0, resource.1.clone()))
@@ -205,19 +218,15 @@ impl RuntimeActivityStats {
         // continuing op
       } else {
         // before, but not after
-        disappeared.push(RuntimeActivity::AsyncOp(
-          op.0,
-          ops[op.1 as usize].decl.name.to_owned(),
-        ));
+        disappeared
+          .push(RuntimeActivity::AsyncOp(op.0, ops[op.1 as usize].decl.name));
       }
     }
     for op in after.ops.ops.iter() {
       if a.contains(op.0 as usize) {
         // after but not before
-        appeared.push(RuntimeActivity::AsyncOp(
-          op.0,
-          ops[op.1 as usize].decl.name.to_owned(),
-        ));
+        appeared
+          .push(RuntimeActivity::AsyncOp(op.0, ops[op.1 as usize].decl.name));
       }
     }
 


Test patch:
diff --git a/testing/checkin/runner/ops.rs b/testing/checkin/runner/ops.rs
index d49610b..5acd4f2 100644
--- a/testing/checkin/runner/ops.rs
+++ b/testing/checkin/runner/ops.rs
@@ -41,7 +41,7 @@ pub fn op_stats_capture(#[string] name: String, state: Rc<RefCell<OpState>>) {
     .borrow()
     .borrow::<RuntimeActivityStatsFactory>()
     .clone();
-  let data = stats.capture(RuntimeActivityStatsFilter::all());
+  let data = stats.capture(&RuntimeActivityStatsFilter::all());
   let mut state = state.borrow_mut();
   let test_data = state.borrow_mut::<TestData>();
   test_data.insert(name, data);