Repository: denoland/deno_core
Pull request #244 (2023-09-27 20:38:57)

feat(ops): zero-cost-if-unused, pluggable metrics for op2
Introduces a basic metrics API for op2 that is zero cost when unused, pluggable-per-op and a flexible system that we can add more to in the future.

As we remove op1 from the system, we will likely be able to simplify the async portion of this metrics system. Right now we're juggling the old and new op systems.

The metrics function gets a reference to the op's per-realm OpCtx, and a metrics event (`Enter`, `Leave`, `Exception`). Async function also have `LeaveAsync`, `ExceptionAsync` if they return a value/error asynchronously.

```rust
  let mut runtime = JsRuntime::new(RuntimeOptions {
    extensions: vec![test_ext::init_ops()],
    op_metrics_fn: Some(Box::new(move |op| {
      if !op.name.starts_with("op_async") && !op.name.starts_with("op_sync") {
        return None;
      }
      Some(Rc::new(move |op, metrics| {
        println!("{} {:?}", op.name, metrics);
      }))
    })),
    ..Default::default()
  });
```


Patch:
diff --git a/.gitattributes b/.gitattributes
index 17ac14d..2e6f3ad 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,4 @@
 # Use Unix line endings in all text files.
 * text=auto eol=lf
 *.png -text
+*.out linguist-generated=true
diff --git a/Cargo.lock b/Cargo.lock
index d0b20af..8a13736 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -297,6 +297,7 @@ dependencies = [
  "once_cell",
  "parking_lot",
  "pin-project",
+ "pretty_assertions",
  "serde",
  "serde_json",
  "serde_v8",
diff --git a/core/Cargo.toml b/core/Cargo.toml
index 18640e0..da6d7ba 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -49,6 +49,7 @@ path = "examples/http_bench_json_ops/main.rs"
 cooked-waker.workspace = true
 deno_ast.workspace = true
 bencher.workspace = true
+pretty_assertions.workspace = true
 
 [[bench]]
 name = "ops_sync"
diff --git a/core/extensions.rs b/core/extensions.rs
index 2cd0ec6..f9f9840 100644
--- a/core/extensions.rs
+++ b/core/extensions.rs
@@ -99,8 +99,14 @@ pub struct OpDecl {
   pub is_unstable: bool,
   pub is_v8: bool,
   pub arg_count: u8,
-  pub(crate) v8_fn_ptr: OpFnRef,
+  /// The slow dispatch call. If metrics are disabled, the `v8::Function` is created with this callback.
+  pub(crate) slow_fn: OpFnRef,
+  /// The slow dispatch call with metrics enabled. If metrics are enabled, the `v8::Function` is created with this callback.
+  pub(crate) slow_fn_with_metrics: OpFnRef,
+  /// The fast dispatch call. If metrics are disabled, the `v8::Function`'s fastcall is created with this callback.
   pub(crate) fast_fn: Option<FastFunction>,
+  /// The fast dispatch call with metrics enabled. If metrics are enabled, the `v8::Function`'s fastcall is created with this callback.
+  pub(crate) fast_fn_with_metrics: Option<FastFunction>,
 }
 
 impl OpDecl {
@@ -112,7 +118,7 @@ impl OpDecl {
     is_unstable: bool,
     is_v8: bool,
     arg_count: u8,
-    v8_fn_ptr: OpFnRef,
+    slow_fn: OpFnRef,
     fast_fn: Option<FastFunction>,
   ) -> Self {
     Self {
@@ -122,8 +128,35 @@ impl OpDecl {
       is_unstable,
       is_v8,
       arg_count,
-      v8_fn_ptr,
+      slow_fn,
+      slow_fn_with_metrics: slow_fn,
       fast_fn,
+      fast_fn_with_metrics: fast_fn,
+    }
+  }
+
+  /// For use by internal op implementation only.
+  #[doc(hidden)]
+  pub const fn new_internal_op2(
+    name: &'static str,
+    is_async: bool,
+    arg_count: u8,
+    slow_fn: OpFnRef,
+    slow_fn_with_metrics: OpFnRef,
+    fast_fn: Option<FastFunction>,
+    fast_fn_with_metrics: Option<FastFunction>,
+  ) -> Self {
+    Self {
+      name,
+      enabled: true,
+      is_async,
+      is_unstable: false,
+      is_v8: false,
+      arg_count,
+      slow_fn,
+      slow_fn_with_metrics,
+      fast_fn,
+      fast_fn_with_metrics,
     }
   }
 
@@ -141,8 +174,10 @@ impl OpDecl {
   /// `OpDecl`.
   pub const fn with_implementation_from(self, from: &Self) -> Self {
     Self {
-      v8_fn_ptr: from.v8_fn_ptr,
+      slow_fn: from.slow_fn,
+      slow_fn_with_metrics: from.slow_fn_with_metrics,
       fast_fn: from.fast_fn,
+      fast_fn_with_metrics: from.fast_fn_with_metrics,
       ..self
     }
   }
diff --git a/core/lib.rs b/core/lib.rs
index f6e3f07..8668f0d 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -140,6 +140,7 @@ pub mod _ops {
   pub use super::extensions::OpDecl;
   pub use super::ops::to_op_result;
   pub use super::ops::OpCtx;
+  pub use super::ops::OpMetricsEvent;
   pub use super::ops::OpResult;
   pub use super::runtime::ops::*;
   pub use super::runtime::ops_rust_to_v8::*;
diff --git a/core/ops.rs b/core/ops.rs
index b4fe7bd..3aa0750 100644
--- a/core/ops.rs
+++ b/core/ops.rs
@@ -28,6 +28,7 @@ use v8::Isolate;
 
 pub type PromiseId = i32;
 pub type OpId = u16;
+pub struct PendingOp(pub PromiseId, pub OpId, pub OpResult, pub bool);
 
 #[pin_project]
 pub struct OpCall<F: Future<Output = OpResult>> {
@@ -50,7 +51,7 @@ impl<F: Future<Output = OpResult>> OpCall<F> {
 }
 
 impl<F: Future<Output = OpResult>> Future for OpCall<F> {
-  type Output = (PromiseId, OpId, OpResult);
+  type Output = PendingOp;
 
   fn poll(
     self: std::pin::Pin<&mut Self>,
@@ -59,7 +60,9 @@ impl<F: Future<Output = OpResult>> Future for OpCall<F> {
     let promise_id = self.promise_id;
     let op_id = self.op_id;
     let fut = self.project().fut;
-    fut.poll(cx).map(move |res| (promise_id, op_id, res))
+    fut
+      .poll(cx)
+      .map(move |res| PendingOp(promise_id, op_id, res, false))
   }
 }
 
@@ -120,6 +123,22 @@ pub fn to_op_result<R: Serialize + 'static>(
   }
 }
 
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum OpMetricsEvent {
+  /// Entered an op.
+  Dispatched,
+  /// Left an op synchronously.
+  Completed,
+  /// Left an op asynchronously.
+  CompletedAsync,
+  /// Left an op synchronously with an exception.
+  Error,
+  /// Left an op asynchronously with an exception.
+  ErrorAsync,
+}
+
+pub type OpMetricsFn = Rc<dyn Fn(&OpDecl, OpMetricsEvent)>;
+
 /// Per-op context.
 ///
 // Note: We don't worry too much about the size of this struct because it's allocated once per realm, and is
@@ -133,12 +152,14 @@ pub struct OpCtx {
   pub decl: Rc<OpDecl>,
   pub fast_fn_c_info: Option<NonNull<v8::fast_api::CFunctionInfo>>,
   pub runtime_state: Weak<RefCell<JsRuntimeState>>,
+  pub(crate) metrics_fn: Option<OpMetricsFn>,
   pub(crate) context_state: Rc<RefCell<ContextState>>,
   /// If the last fast op failed, stores the error to be picked up by the slow op.
   pub(crate) last_fast_error: UnsafeCell<Option<AnyError>>,
 }
 
 impl OpCtx {
+  #[allow(clippy::too_many_arguments)]
   pub(crate) fn new(
     id: OpId,
     isolate: *mut Isolate,
@@ -147,10 +168,21 @@ impl OpCtx {
     state: Rc<RefCell<OpState>>,
     runtime_state: Weak<RefCell<JsRuntimeState>>,
     get_error_class_fn: GetErrorClassFn,
+    metrics_fn: Option<OpMetricsFn>,
   ) -> Self {
     let mut fast_fn_c_info = None;
 
-    if let Some(fast_fn) = &decl.fast_fn {
+    // If we want metrics for this function, create the fastcall `CFunctionInfo` from the metrics
+    // `FastFunction`. For some extremely fast ops, the parameter list may change for the metrics
+    // version and require a slightly different set of arguments (for example, it may need the fastcall
+    // callback information to get the `OpCtx`).
+    let fast_fn = if metrics_fn.is_some() {
+      &decl.fast_fn
+    } else {
+      &decl.fast_fn_with_metrics
+    };
+
+    if let Some(fast_fn) = fast_fn {
       let args = CTypeInfo::new_from_slice(fast_fn.args);
       let ret = CTypeInfo::new(fast_fn.return_type);
 
@@ -169,7 +201,7 @@ impl OpCtx {
       fast_fn_c_info = Some(c_fn);
     }
 
-    OpCtx {
+    Self {
       id,
       state,
       get_error_class_fn,
@@ -179,9 +211,14 @@ impl OpCtx {
       fast_fn_c_info,
       last_fast_error: UnsafeCell::new(None),
       isolate,
+      metrics_fn,
     }
   }
 
+  pub fn metrics_enabled(&self) -> bool {
+    self.metrics_fn.is_some()
+  }
+
   /// This takes the last error from an [`OpCtx`], assuming that no other code anywhere
   /// can hold a `&mut` to the last_fast_error field.
   ///
diff --git a/core/runtime/bindings.rs b/core/runtime/bindings.rs
index 6b32b59..719659a 100644
--- a/core/runtime/bindings.rs
+++ b/core/runtime/bindings.rs
@@ -43,18 +43,34 @@ pub(crate) fn external_references(
   });
 
   for ctx in ops {
-    let ctx_ptr = ctx as *const OpCtx as _;
-    references.push(v8::ExternalReference { pointer: ctx_ptr });
-    references.push(v8::ExternalReference {
-      function: ctx.decl.v8_fn_ptr,
-    });
-    if let Some(fast_fn) = &ctx.decl.fast_fn {
+    if ctx.metrics_enabled() {
+      let ctx_ptr = ctx as *const OpCtx as _;
+      references.push(v8::ExternalReference { pointer: ctx_ptr });
       references.push(v8::ExternalReference {
-        pointer: fast_fn.function as _,
+        function: ctx.decl.slow_fn_with_metrics,
       });
+      if let Some(fast_fn) = &ctx.decl.fast_fn_with_metrics {
+        references.push(v8::ExternalReference {
+          pointer: fast_fn.function as _,
+        });
+        references.push(v8::ExternalReference {
+          pointer: ctx.fast_fn_c_info.unwrap().as_ptr() as _,
+        });
+      }
+    } else {
+      let ctx_ptr = ctx as *const OpCtx as _;
+      references.push(v8::ExternalReference { pointer: ctx_ptr });
       references.push(v8::ExternalReference {
-        pointer: ctx.fast_fn_c_info.unwrap().as_ptr() as _,
+        function: ctx.decl.slow_fn,
       });
+      if let Some(fast_fn) = &ctx.decl.fast_fn {
+        references.push(v8::ExternalReference {
+          pointer: fast_fn.function as _,
+        });
+        references.push(v8::ExternalReference {
+          pointer: ctx.fast_fn_c_info.unwrap().as_ptr() as _,
+        });
+      }
     }
   }
 
@@ -190,12 +206,22 @@ fn op_ctx_function<'s>(
   let v8name =
     v8::String::new_external_onebyte_static(scope, op_ctx.decl.name.as_bytes())
       .unwrap();
+
+  let (slow_fn, fast_fn) = if op_ctx.metrics_enabled() {
+    (
+      op_ctx.decl.slow_fn_with_metrics,
+      op_ctx.decl.fast_fn_with_metrics,
+    )
+  } else {
+    (op_ctx.decl.slow_fn, op_ctx.decl.fast_fn)
+  };
+
   let builder: v8::FunctionBuilder<v8::FunctionTemplate> =
-    v8::FunctionTemplate::builder_raw(op_ctx.decl.v8_fn_ptr)
+    v8::FunctionTemplate::builder_raw(slow_fn)
       .data(external.into())
       .length(op_ctx.decl.arg_count as i32);
 
-  let template = if let Some(fast_function) = &op_ctx.decl.fast_fn {
+  let template = if let Some(fast_function) = &fast_fn {
     builder.build_fast(
       scope,
       fast_function,
diff --git a/core/runtime/jsrealm.rs b/core/runtime/jsrealm.rs
index 26832f2..3f2463c 100644
--- a/core/runtime/jsrealm.rs
+++ b/core/runtime/jsrealm.rs
@@ -10,11 +10,9 @@ use crate::modules::ModuleId;
 use crate::modules::ModuleLoadId;
 use crate::modules::ModuleMap;
 use crate::ops::OpCtx;
+use crate::ops::PendingOp;
 use crate::runtime::JsRuntimeState;
 use crate::JsRuntime;
-use crate::OpId;
-use crate::OpResult;
-use crate::PromiseId;
 use anyhow::Error;
 use deno_unsync::JoinSet;
 use futures::channel::oneshot;
@@ -76,7 +74,7 @@ pub(crate) struct ContextState {
   pending_dyn_mod_evaluate: Vec<DynImportModEvaluate>,
   pub(crate) pending_mod_evaluate: Option<ModEvaluate>,
   pub(crate) unrefed_ops: HashSet<i32, BuildHasherDefault<IdentityHasher>>,
-  pub(crate) pending_ops: JoinSet<(PromiseId, OpId, OpResult)>,
+  pub(crate) pending_ops: JoinSet<PendingOp>,
   // We don't explicitly re-read this prop but need the slice to live alongside
   // the context
   pub(crate) op_ctxs: Box<[OpCtx]>,
diff --git a/core/runtime/jsruntime.rs b/core/runtime/jsruntime.rs
index bff5ef4..e89c97c 100644
--- a/core/runtime/jsruntime.rs
+++ b/core/runtime/jsruntime.rs
@@ -2,6 +2,7 @@
 
 use super::bindings;
 use super::jsrealm::JsRealmInner;
+use super::ops::dispatch_metrics_async;
 use super::snapshot_util;
 use crate::error::exception_to_err_result;
 use crate::error::generic_error;
@@ -361,6 +362,8 @@ fn v8_init(
   v8::V8::initialize();
 }
 
+pub type OpMetricsFactoryFn = Box<dyn Fn(&OpDecl) -> Option<OpMetricsFn>>;
+
 #[derive(Default)]
 pub struct RuntimeOptions {
   /// Source map reference for errors.
@@ -377,6 +380,10 @@ pub struct RuntimeOptions {
   /// executed tries to load modules.
   pub module_loader: Option<Rc<dyn ModuleLoader>>,
 
+  /// Provide a function that may optionally provide a metrics collector
+  /// for a given op.
+  pub op_metrics_fn: Option<OpMetricsFactoryFn>,
+
   /// JsRuntime extensions, not to be confused with ES modules.
   /// Only ops registered by extensions will be initialized. If you need
   /// to execute JS code from extensions, pass source files in `js` or `esm`
@@ -594,6 +601,8 @@ impl JsRuntime {
       .into_iter()
       .enumerate()
       .map(|(id, decl)| {
+        let metrics_fn =
+          options.op_metrics_fn.as_ref().and_then(|f| (f)(&decl));
         OpCtx::new(
           id as u16,
           std::ptr::null_mut(),
@@ -602,6 +611,7 @@ impl JsRuntime {
           op_state.clone(),
           weak.clone(),
           options.get_error_class_fn.unwrap_or(&|_| "Error"),
+          metrics_fn,
         )
       })
       .collect::<Vec<_>>()
@@ -832,6 +842,7 @@ impl JsRuntime {
             op_ctx.state.clone(),
             op_ctx.runtime_state.clone(),
             op_ctx.get_error_class_fn,
+            op_ctx.metrics_fn.clone(),
           )
         })
         .collect();
@@ -1965,7 +1976,7 @@ impl JsRuntime {
           break;
         };
         // TODO(mmastrac): If this task is really errored, things could be pretty bad
-        let (promise_id, op_id, resp) = item.unwrap();
+        let PendingOp(promise_id, op_id, resp, metrics_event) = item.unwrap();
         state
           .borrow()
           .op_state
@@ -1975,7 +1986,22 @@ impl JsRuntime {
         context_state.unrefed_ops.remove(&promise_id);
         dispatched_ops |= true;
         args.push(v8::Integer::new(scope, promise_id).into());
-        args.push(match resp.to_v8(scope) {
+        let was_error = matches!(resp, OpResult::Err(_));
+        let res = resp.to_v8(scope);
+        if metrics_event {
+          if res.is_ok() && !was_error {
+            dispatch_metrics_async(
+              &context_state.op_ctxs[op_id as usize],
+              OpMetricsEvent::CompletedAsync,
+            );
+          } else {
+            dispatch_metrics_async(
+              &context_state.op_ctxs[op_id as usize],
+              OpMetricsEvent::ErrorAsync,
+            );
+          }
+        }
+        args.push(match res {
           Ok(v) => v,
           Err(e) => OpResult::Err(OpError::new(&|_| "TypeError", e.into()))
             .to_v8(scope)
diff --git a/core/runtime/ops.rs b/core/runtime/ops.rs
index decc871..cafa859 100644
--- a/core/runtime/ops.rs
+++ b/core/runtime/ops.rs
@@ -111,10 +111,13 @@ pub fn queue_async_op<'s>(
   // );
 
   let id = ctx.id;
+  let metrics = ctx.metrics_enabled();
 
   // TODO(mmastrac): We have to poll every future here because that assumption is baked into a large number
   // of ops. If we can figure out a way around this, we can remove this call to boxed_local and save a malloc per future.
-  let mut pinned = op.map(move |res| (promise_id, id, res)).boxed_local();
+  let mut pinned = op
+    .map(move |res| PendingOp(promise_id, id, res, metrics))
+    .boxed_local();
 
   match pinned.poll_unpin(&mut Context::from_waker(noop_waker_ref())) {
     Poll::Pending => {}
@@ -146,13 +149,15 @@ pub fn map_async_op_infallible<R: 'static>(
 ) -> Option<R> {
   let id = ctx.id;
   ctx.state.borrow().tracker.track_async(id);
+  let metrics = ctx.metrics_enabled();
 
   if lazy {
     let mapper = move |r| {
-      (
+      PendingOp(
         promise_id,
         id,
         OpResult::Op2Temp(Box::new(move |scope| rv_map(scope, r))),
+        metrics,
       )
     };
     ctx
@@ -170,6 +175,9 @@ pub fn map_async_op_infallible<R: 'static>(
   match pinned.poll_unpin(&mut Context::from_waker(noop_waker_ref())) {
     Poll::Pending => {}
     Poll::Ready(res) => {
+      if ctx.metrics_enabled() {
+        dispatch_metrics_async(ctx, OpMetricsEvent::Completed);
+      }
       ctx.state.borrow_mut().tracker.track_async_completed(ctx.id);
       return Some(res.2);
     }
@@ -180,10 +188,11 @@ pub fn map_async_op_infallible<R: 'static>(
     .borrow_mut()
     .pending_ops
     .spawn(pinned.map(move |r| {
-      (
+      PendingOp(
         r.0,
         r.1,
         OpResult::Op2Temp(Box::new(move |scope| rv_map(scope, r.2))),
+        metrics,
       )
     }));
   None
@@ -202,20 +211,23 @@ pub fn map_async_op_fallible<R: 'static, E: Into<Error> + 'static>(
 ) -> Option<Result<R, E>> {
   let id = ctx.id;
   ctx.state.borrow().tracker.track_async(id);
+  let metrics = ctx.metrics_enabled();
 
   if lazy {
     let get_class = ctx.get_error_class_fn;
     ctx.context_state.borrow_mut().pending_ops.spawn(op.map(
       move |r| match r {
-        Ok(v) => (
+        Ok(v) => PendingOp(
           promise_id,
           id,
           OpResult::Op2Temp(Box::new(move |scope| rv_map(scope, v))),
+          metrics,
         ),
-        Err(err) => (
+        Err(err) => PendingOp(
           promise_id,
           id,
           OpResult::Err(OpError::new(get_class, err.into())),
+          metrics,
         ),
       },
     ));
@@ -229,6 +241,13 @@ pub fn map_async_op_fallible<R: 'static, E: Into<Error> + 'static>(
   match pinned.poll_unpin(&mut Context::from_waker(noop_waker_ref())) {
     Poll::Pending => {}
     Poll::Ready(res) => {
+      if ctx.metrics_enabled() {
+        if res.2.is_err() {
+          dispatch_metrics_async(ctx, OpMetricsEvent::Error);
+        } else {
+          dispatch_metrics_async(ctx, OpMetricsEvent::Completed);
+        }
+      }
       ctx.state.borrow_mut().tracker.track_async_completed(ctx.id);
       return Some(res.2);
     }
@@ -237,19 +256,35 @@ pub fn map_async_op_fallible<R: 'static, E: Into<Error> + 'static>(
   let get_class = ctx.get_error_class_fn;
   ctx.context_state.borrow_mut().pending_ops.spawn(pinned.map(
     move |r| match r.2 {
-      Ok(v) => (
+      Ok(v) => PendingOp(
         r.0,
         r.1,
         OpResult::Op2Temp(Box::new(move |scope| rv_map(scope, v))),
+        metrics,
+      ),
+      Err(err) => PendingOp(
+        r.0,
+        r.1,
+        OpResult::Err(OpError::new(get_class, err.into())),
+        metrics,
       ),
-      Err(err) => {
-        (r.0, r.1, OpResult::Err(OpError::new(get_class, err.into())))
-      }
     },
   ));
   None
 }
 
+pub fn dispatch_metrics_fast(opctx: &OpCtx, metrics: OpMetricsEvent) {
+  (opctx.metrics_fn.as_ref().unwrap())(&opctx.decl, metrics)
+}
+
+pub fn dispatch_metrics_slow(opctx: &OpCtx, metrics: OpMetricsEvent) {
+  (opctx.metrics_fn.as_ref().unwrap())(&opctx.decl, metrics)
+}
+
+pub fn dispatch_metrics_async(opctx: &OpCtx, metrics: OpMetricsEvent) {
+  (opctx.metrics_fn.as_ref().unwrap())(&opctx.decl, metrics)
+}
+
 macro_rules! try_number_some {
   ($n:ident $type:ident $is:ident) => {
     if $n.$is() {
diff --git a/ops/op2/dispatch_async.rs b/ops/op2/dispatch_async.rs
index 04240af..7ad5067 100644
--- a/ops/op2/dispatch_async.rs
+++ b/ops/op2/dispatch_async.rs
@@ -79,7 +79,10 @@ pub(crate) fn generate_dispatch_async(
 
   args.extend(deferred);
   args.extend(call(generator_state)?);
-  output.extend(gs_quote!(generator_state(result) => {
+  output.extend(gs_quote!(generator_state(deno_core, result, opctx) => {
+    if #opctx.metrics_enabled() {
+      #deno_core::_ops::dispatch_metrics_async(&#opctx, #deno_core::_ops::OpMetricsEvent::Dispatched);
+    }
     let #result = {
       #args
     };
@@ -90,10 +93,13 @@ pub(crate) fn generate_dispatch_async(
     RetVal::ResultFuture(_) | RetVal::ResultFutureResult(_)
   ) {
     let exception = throw_exception(generator_state)?;
-    output.extend(gs_quote!(generator_state(result) => {
+    output.extend(gs_quote!(generator_state(deno_core, opctx, result) => {
       let #result = match #result {
         Ok(#result) => #result,
         Err(err) => {
+          if #opctx.metrics_enabled() {
+            #deno_core::_ops::dispatch_metrics_async(&#opctx, #deno_core::_ops::OpMetricsEvent::Error);
+          }
           // Handle eager error -- this will leave only a Future<R> or Future<Result<R>>
           #exception
         }
@@ -143,15 +149,20 @@ pub(crate) fn generate_dispatch_async(
   };
 
   Ok(
-    gs_quote!(generator_state(deno_core, info, slow_function) => {
+    gs_quote!(generator_state(deno_core, info, slow_function, slow_function_metrics) => {
       extern "C" fn #slow_function(#info: *const #deno_core::v8::FunctionCallbackInfo) {
-      #with_scope
-      #with_retval
-      #with_args
-      #with_opctx
-      #with_opstate
-
-      #output
-    }}),
+        #with_scope
+        #with_retval
+        #with_args
+        #with_opctx
+        #with_opstate
+
+        #output
+      }
+
+      extern "C" fn #slow_function_metrics(#info: *const #deno_core::v8::FunctionCallbackInfo) {
+        Self::#slow_function(#info)
+      }
+    }),
   )
 }
diff --git a/ops/op2/dispatch_fast.rs b/ops/op2/dispatch_fast.rs
index 6d06651..6371741 100644
--- a/ops/op2/dispatch_fast.rs
+++ b/ops/op2/dispatch_fast.rs
@@ -4,6 +4,7 @@ use super::dispatch_shared::fast_api_typed_array_to_buffer;
 use super::dispatch_shared::v8_intermediate_to_arg;
 use super::dispatch_shared::v8_to_arg;
 use super::dispatch_shared::v8slice_to_buffer;
+use super::generator_state::gs_quote;
 use super::generator_state::GeneratorState;
 use super::signature::Arg;
 use super::signature::Buffer;
@@ -138,7 +139,7 @@ impl V8FastCallType {
 pub fn generate_dispatch_fast(
   generator_state: &mut GeneratorState,
   signature: &ParsedSignature,
-) -> Result<Option<(TokenStream, TokenStream)>, V8MappingError> {
+) -> Result<Option<(TokenStream, TokenStream, TokenStream)>, V8MappingError> {
   enum Input<'a> {
     Concrete(V8FastCallType),
     Virtual(&'a Arg),
@@ -191,12 +192,9 @@ pub fn generate_dispatch_fast(
   }
 
   let GeneratorState {
-    deno_core,
     result,
     fast_function,
-    opctx,
-    js_runtime_state,
-    fast_api_callback_options,
+    fast_function_metrics,
     needs_fast_opctx,
     needs_fast_api_callback_options,
     needs_fast_js_runtime_state,
@@ -208,7 +206,7 @@ pub fn generate_dispatch_fast(
     RetVal::Result(_) => {
       *needs_fast_api_callback_options = true;
       *needs_fast_opctx = true;
-      quote! {
+      gs_quote!(generator_state(fast_api_callback_options, opctx) => {
         let #result = match #result {
           Ok(#result) => #result,
           Err(err) => {
@@ -234,47 +232,60 @@ pub fn generate_dispatch_fast(
             return unsafe { std::mem::zeroed() };
           }
         };
-      }
+      })
     }
     _ => todo!(),
   };
 
   let with_js_runtime_state = if *needs_fast_js_runtime_state {
     *needs_fast_opctx = true;
-    quote! {
+    gs_quote!(generator_state(js_runtime_state, opctx) => {
       let #js_runtime_state = std::rc::Weak::upgrade(&#opctx.runtime_state).unwrap();
-    }
+    })
   } else {
     quote!()
   };
 
   let with_opctx = if *needs_fast_opctx {
     *needs_fast_api_callback_options = true;
-    quote!(
+    gs_quote!(generator_state(deno_core, opctx, fast_api_callback_options) => {
       let #opctx = unsafe {
         &*(#deno_core::v8::Local::<#deno_core::v8::External>::cast(unsafe { #fast_api_callback_options.data.data }).value()
             as *const #deno_core::_ops::OpCtx)
       };
-    )
+    })
   } else {
     quote!()
   };
 
+  let mut fastcall_metrics_names = fastcall_names.clone();
+  let mut fastcall_metrics_types = fastcall_types.clone();
+  let mut input_types_metrics = input_types.clone();
+
   let with_fast_api_callback_options = if *needs_fast_api_callback_options {
     input_types.push(V8FastCallType::CallbackOptions.quote_type());
-    fastcall_types
-      .push(V8FastCallType::CallbackOptions.quote_rust_type(deno_core));
-    fastcall_names.push(fast_api_callback_options.clone());
-    quote! {
+    fastcall_types.push(
+      V8FastCallType::CallbackOptions
+        .quote_rust_type(&generator_state.deno_core),
+    );
+    fastcall_names.push(generator_state.fast_api_callback_options.clone());
+    gs_quote!(generator_state(fast_api_callback_options) => {
       let #fast_api_callback_options = unsafe { &mut *#fast_api_callback_options };
-    }
+    })
   } else {
     quote!()
   };
 
+  input_types_metrics.push(V8FastCallType::CallbackOptions.quote_type());
+  fastcall_metrics_types.push(
+    V8FastCallType::CallbackOptions.quote_rust_type(&generator_state.deno_core),
+  );
+  fastcall_metrics_names
+    .push(generator_state.fast_api_callback_options.clone());
+
   let output_type = output.quote_ctype();
 
-  let fast_definition = quote! {
+  let fast_definition = gs_quote!(generator_state(deno_core) => {
     use #deno_core::v8::fast_api::Type;
     use #deno_core::v8::fast_api::CType;
     // TODO(mmastrac): We're setting this up for future success but returning
@@ -284,15 +295,44 @@ pub fn generate_dispatch_fast(
       #output_type,
       Self::#fast_function as *const ::std::ffi::c_void
     )
-  };
+  });
+
+  let fast_definition_metrics = gs_quote!(generator_state(deno_core) => {
+    use #deno_core::v8::fast_api::Type;
+    use #deno_core::v8::fast_api::CType;
+    // TODO(mmastrac): We're setting this up for future success but returning
+    // u64/i64 from fastcall functions does not work. Test again in the future.
+    #deno_core::v8::fast_api::FastFunction::new_with_bigint(
+      &[ Type::V8Value, #( #input_types_metrics ),* ],
+      #output_type,
+      Self::#fast_function_metrics as *const ::std::ffi::c_void
+    )
+  });
 
   // Ensure that we have the same types in the fast function definition as we do in the signature
   debug_assert!(fastcall_types.len() == input_types.len());
 
-  let output_type = output.quote_rust_type(deno_core);
+  let output_type = output.quote_rust_type(&generator_state.deno_core);
   // We don't want clippy to trigger warnings on number of arguments of the fastcall
   // function -- these will still trigger on our normal call function, however.
-  let fast_fn = quote!(
+  let fast_fn = gs_quote!(generator_state(deno_core, fast_api_callback_options) => {
+    #[allow(clippy::too_many_arguments)]
+    fn #fast_function_metrics(
+      this: #deno_core::v8::Local<#deno_core::v8::Object>,
+      #( #fastcall_metrics_names: #fastcall_metrics_types, )*
+    ) -> #output_type {
+      let #fast_api_callback_options = unsafe { &mut *#fast_api_callback_options };
+      let opctx = unsafe {
+          &*(#deno_core::v8::Local::<#deno_core::v8::External>::cast(
+            unsafe { #fast_api_callback_options.data.data }
+          ).value() as *const #deno_core::_ops::OpCtx)
+      };
+      #deno_core::_ops::dispatch_metrics_fast(&opctx, #deno_core::_ops::OpMetricsEvent::Dispatched);
+      let res = Self::#fast_function( this, #( #fastcall_names, )* );
+      #deno_core::_ops::dispatch_metrics_fast(&opctx, #deno_core::_ops::OpMetricsEvent::Completed);
+      res
+    }
+
     #[allow(clippy::too_many_arguments)]
     fn #fast_function(
       _: #deno_core::v8::Local<#deno_core::v8::Object>,
@@ -309,9 +349,9 @@ pub fn generate_dispatch_fast(
       // Result may need a simple cast (eg: SMI u32->i32)
       #result as _
     }
-  );
+  });
 
-  Ok(Some((fast_definition, fast_fn)))
+  Ok(Some((fast_definition, fast_definition_metrics, fast_fn)))
 }
 
 #[allow(clippy::too_many_arguments)]
diff --git a/ops/op2/dispatch_slow.rs b/ops/op2/dispatch_slow.rs
index dc46d6b..a556d80 100644
--- a/ops/op2/dispatch_slow.rs
+++ b/ops/op2/dispatch_slow.rs
@@ -120,7 +120,7 @@ pub(crate) fn generate_dispatch_slow(
   };
 
   Ok(
-    gs_quote!(generator_state(deno_core, info, slow_function) => {
+    gs_quote!(generator_state(deno_core, opctx, info, slow_function, slow_function_metrics) => {
       extern "C" fn #slow_function(#info: *const #deno_core::v8::FunctionCallbackInfo) {
         #with_scope
         #with_retval
@@ -132,6 +132,19 @@ pub(crate) fn generate_dispatch_slow(
 
         #output
       }
+      extern "C" fn #slow_function_metrics(#info: *const #deno_core::v8::FunctionCallbackInfo) {
+        let args = #deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+          &*info
+        });
+        let #opctx = unsafe {
+            &*(#deno_core::v8::Local::<#deno_core::v8::External>::cast(args.data()).value()
+                as *const #deno_core::_ops::OpCtx)
+        };
+
+        #deno_core::_ops::dispatch_metrics_slow(&#opctx, #deno_core::_ops::OpMetricsEvent::Dispatched);
+        Self::#slow_function(#info);
+        #deno_core::_ops::dispatch_metrics_slow(&#opctx, #deno_core::_ops::OpMetricsEvent::Completed);
+      }
     }),
   )
 }
diff --git a/ops/op2/generator_state.rs b/ops/op2/generator_state.rs
index d904b89..c8de5de 100644
--- a/ops/op2/generator_state.rs
+++ b/ops/op2/generator_state.rs
@@ -31,8 +31,12 @@ pub struct GeneratorState {
   pub retval: Ident,
   /// The "slow" function (ie: the one that isn't a fastcall)
   pub slow_function: Ident,
+  /// The "slow" function (ie: the one that isn't a fastcall)
+  pub slow_function_metrics: Ident,
   /// The "fast" function (ie: a fastcall)
   pub fast_function: Ident,
+  /// The "fast" function (ie: a fastcall)
+  pub fast_function_metrics: Ident,
   /// The async function promise ID argument
   pub promise_id: Ident,
 
diff --git a/ops/op2/mod.rs b/ops/op2/mod.rs
index e39f172..0cc07c7 100644
--- a/ops/op2/mod.rs
+++ b/ops/op2/mod.rs
@@ -133,7 +133,11 @@ fn generate_op2(
   let js_runtime_state = Ident::new("js_runtime_state", Span::call_site());
   let promise_id = Ident::new("promise_id", Span::call_site());
   let slow_function = Ident::new("v8_fn_ptr", Span::call_site());
+  let slow_function_metrics =
+    Ident::new("v8_fn_ptr_metrics", Span::call_site());
   let fast_function = Ident::new("v8_fn_ptr_fast", Span::call_site());
+  let fast_function_metrics =
+    Ident::new("v8_fn_ptr_fast_metrics", Span::call_site());
   let fast_api_callback_options =
     Ident::new("fast_api_callback_options", Span::call_site());
 
@@ -160,7 +164,9 @@ fn generate_op2(
     retval,
     needs_args,
     slow_function,
+    slow_function_metrics,
     fast_function,
+    fast_function_metrics,
     promise_id,
     needs_retval: false,
     needs_scope: false,
@@ -188,17 +194,21 @@ fn generate_op2(
     _ => {}
   }
 
-  let (fast_definition, fast_fn) =
+  let (fast_definition, fast_definition_metrics, fast_fn) =
     match generate_dispatch_fast(&mut generator_state, &signature)? {
-      Some((fast_definition, fast_fn)) => {
+      Some((fast_definition, fast_metrics_definition, fast_fn)) => {
         if !config.fast && !config.nofast {
           return Err(Op2Error::ShouldBeFast);
         }
         // nofast requires the function to be valid for fast
         if config.nofast {
-          (quote!(None), quote!())
+          (quote!(None), quote!(None), quote!())
         } else {
-          (quote!(Some({#fast_definition})), fast_fn)
+          (
+            quote!(Some({#fast_definition})),
+            quote!(Some({#fast_metrics_definition})),
+            fast_fn,
+          )
         }
       }
       None => {
@@ -208,13 +218,14 @@ fn generate_op2(
         if config.nofast {
           return Err(Op2Error::ShouldNotBeFast("nofast"));
         }
-        (quote!(None), quote!())
+        (quote!(None), quote!(None), quote!())
       }
     };
 
   let GeneratorState {
     deno_core,
     slow_function,
+    slow_function_metrics,
     ..
   } = &generator_state;
 
@@ -241,14 +252,14 @@ fn generate_op2(
 
     impl <#(#generic : #bound),*> #deno_core::_ops::Op for #name <#(#generic),*> {
       const NAME: &'static str = stringify!(#name);
-      const DECL: #deno_core::_ops::OpDecl = #deno_core::_ops::OpDecl::new_internal(
+      const DECL: #deno_core::_ops::OpDecl = #deno_core::_ops::OpDecl::new_internal_op2(
         /*name*/ stringify!(#name),
         /*is_async*/ #is_async,
-        /*is_unstable*/ false,
-        /*is_v8*/ false,
         /*arg_count*/ #arg_count as u8,
-        /*v8_fn_ptr*/ Self::#slow_function as _,
+        /*slow_fn*/ Self::#slow_function as _,
+        /*slow_fn_metrics*/ Self::#slow_function_metrics as _,
         /*fast_fn*/ #fast_definition,
+        /*fast_fn_metrics*/ #fast_definition_metrics,
       );
     }
 


Test patch:
diff --git a/core/runtime/tests/ops.rs b/core/runtime/tests/ops.rs
index 2c1e526..6631722 100644
--- a/core/runtime/tests/ops.rs
+++ b/core/runtime/tests/ops.rs
@@ -1,14 +1,17 @@
 // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
 use crate as deno_core;
+use crate::error::AnyError;
 use crate::extensions::Op;
 use crate::extensions::OpDecl;
 use crate::modules::StaticModuleLoader;
 use crate::runtime::tests::setup;
 use crate::runtime::tests::Mode;
 use crate::*;
+use anyhow::bail;
 use anyhow::Error;
 use deno_ops::op;
 use log::debug;
+use pretty_assertions::assert_eq;
 use std::cell::RefCell;
 use std::rc::Rc;
 use std::sync::atomic::Ordering;
@@ -581,3 +584,102 @@ await op_void_async_deferred();
   };
   res.unwrap();
 }
+
+#[tokio::test]
+pub async fn test_op_metrics() {
+  #[op2(async, core)]
+  pub async fn op_async() {
+    println!("op_async!");
+  }
+
+  #[op2(async, core)]
+  pub async fn op_async_deferred() {
+    tokio::task::yield_now().await;
+    println!("op_async_deferred!");
+  }
+
+  #[op2(async, core)]
+  pub async fn op_async_deferred_error() -> Result<(), AnyError> {
+    tokio::task::yield_now().await;
+    println!("op_async_deferred_error!");
+    bail!("dead");
+  }
+
+  #[op2(async, core)]
+  pub async fn op_async_error() -> Result<(), AnyError> {
+    println!("op_async_error!");
+    bail!("dead");
+  }
+
+  #[op2(core, fast)]
+  pub fn op_sync() {
+    println!("op_sync!");
+  }
+
+  deno_core::extension!(
+    test_ext,
+    ops = [
+      op_async,
+      op_async_error,
+      op_async_deferred,
+      op_async_deferred_error,
+      op_sync
+    ],
+  );
+
+  let out = Rc::new(RefCell::new(vec![]));
+
+  let out_clone = out.clone();
+  let mut runtime = JsRuntime::new(RuntimeOptions {
+    extensions: vec![test_ext::init_ops()],
+    op_metrics_fn: Some(Box::new(move |op| {
+      if !op.name.starts_with("op_async") && !op.name.starts_with("op_sync") {
+        return None;
+      }
+      let out_clone = out_clone.clone();
+      Some(Rc::new(move |op, metrics| {
+        let s = format!("{} {:?}", op.name, metrics);
+        println!("{s}");
+        out_clone.borrow_mut().push(s);
+      }))
+    })),
+    ..Default::default()
+  });
+
+  let promise = runtime
+  .execute_script_static(
+    "filename.js",
+    r#"
+  const { op_sync, op_async, op_async_error, op_async_deferred, op_async_deferred_error } = Deno.core.ensureFastOps();
+    async function go() {
+      op_sync();
+      await op_async();
+      try { await op_async_error() } catch {}
+      await op_async_deferred();
+      try { await op_async_deferred_error() } catch {}
+    }
+
+    go()
+    "#,
+  )
+  .unwrap();
+  runtime
+    .resolve_value(promise)
+    .await
+    .expect("Failed to await promise");
+  drop(runtime);
+  let out = Rc::try_unwrap(out).unwrap().into_inner().join("\n");
+  assert_eq!(
+    out,
+    r#"op_sync Dispatched
+op_sync Completed
+op_async Dispatched
+op_async Completed
+op_async_error Dispatched
+op_async_error Error
+op_async_deferred Dispatched
+op_async_deferred CompletedAsync
+op_async_deferred_error Dispatched
+op_async_deferred_error ErrorAsync"#
+  );
+}
diff --git a/ops/op2/test_cases/async/async_arg_return.out b/ops/op2/test_cases/async/async_arg_return.out
index c359f0e..6c3a117 100644
--- a/ops/op2/test_cases/async/async_arg_return.out
+++ b/ops/op2/test_cases/async/async_arg_return.out
@@ -4,13 +4,13 @@ pub struct op_async {
 }
 impl deno_core::_ops::Op for op_async {
     const NAME: &'static str = stringify!(op_async);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_async),
         true,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -34,6 +34,12 @@ impl op_async {
             &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
                 as *const deno_core::_ops::OpCtx)
         };
+        if opctx.metrics_enabled() {
+            deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                deno_core::_ops::OpMetricsEvent::Dispatched,
+            );
+        }
         let result = {
             let arg0 = args.get(1usize as i32);
             let Some(arg0) = deno_core::_ops::to_i32_option(&arg0) else {
@@ -63,6 +69,9 @@ impl op_async {
             deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        Self::v8_fn_ptr(info)
+    }
     #[inline(always)]
     pub async fn call(x: i32) -> i32 {
         x
diff --git a/ops/op2/test_cases/async/async_arg_return_result.out b/ops/op2/test_cases/async/async_arg_return_result.out
index ab15550..ecbd4b6 100644
--- a/ops/op2/test_cases/async/async_arg_return_result.out
+++ b/ops/op2/test_cases/async/async_arg_return_result.out
@@ -4,13 +4,13 @@ pub struct op_async {
 }
 impl deno_core::_ops::Op for op_async {
     const NAME: &'static str = stringify!(op_async);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_async),
         true,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -34,6 +34,12 @@ impl op_async {
             &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
                 as *const deno_core::_ops::OpCtx)
         };
+        if opctx.metrics_enabled() {
+            deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                deno_core::_ops::OpMetricsEvent::Dispatched,
+            );
+        }
         let result = {
             let arg0 = args.get(1usize as i32);
             let Some(arg0) = deno_core::_ops::to_i32_option(&arg0) else {
@@ -75,6 +81,9 @@ impl op_async {
             };
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        Self::v8_fn_ptr(info)
+    }
     #[inline(always)]
     pub async fn call(x: i32) -> std::io::Result<i32> {
         Ok(x)
diff --git a/ops/op2/test_cases/async/async_jsbuffer.out b/ops/op2/test_cases/async/async_jsbuffer.out
index b71dffd..26ce082 100644
--- a/ops/op2/test_cases/async/async_jsbuffer.out
+++ b/ops/op2/test_cases/async/async_jsbuffer.out
@@ -4,13 +4,13 @@ pub struct op_async_v8_buffer {
 }
 impl deno_core::_ops::Op for op_async_v8_buffer {
     const NAME: &'static str = stringify!(op_async_v8_buffer);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_async_v8_buffer),
         true,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -34,6 +34,12 @@ impl op_async_v8_buffer {
             &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
                 as *const deno_core::_ops::OpCtx)
         };
+        if opctx.metrics_enabled() {
+            deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                deno_core::_ops::OpMetricsEvent::Dispatched,
+            );
+        }
         let result = {
             let arg0 = args.get(1usize as i32);
             let mut arg0_temp;
@@ -83,6 +89,9 @@ impl op_async_v8_buffer {
             }
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        Self::v8_fn_ptr(info)
+    }
     #[inline(always)]
     pub async fn call(buf: JsBuffer) -> JsBuffer {
         buf
diff --git a/ops/op2/test_cases/async/async_opstate.out b/ops/op2/test_cases/async/async_opstate.out
index dda71c6..8318b78 100644
--- a/ops/op2/test_cases/async/async_opstate.out
+++ b/ops/op2/test_cases/async/async_opstate.out
@@ -4,13 +4,13 @@ pub struct op_async_opstate {
 }
 impl deno_core::_ops::Op for op_async_opstate {
     const NAME: &'static str = stringify!(op_async_opstate);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_async_opstate),
         true,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -35,6 +35,12 @@ impl op_async_opstate {
                 as *const deno_core::_ops::OpCtx)
         };
         let opstate = &opctx.state;
+        if opctx.metrics_enabled() {
+            deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                deno_core::_ops::OpMetricsEvent::Dispatched,
+            );
+        }
         let result = {
             let arg0 = opstate.clone();
             Self::call(arg0)
@@ -63,6 +69,9 @@ impl op_async_opstate {
             };
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        Self::v8_fn_ptr(info)
+    }
     #[inline(always)]
     pub async fn call(state: Rc<RefCell<OpState>>) -> std::io::Result<i32> {
         Ok(*state.borrow().borrow::<i32>())
diff --git a/ops/op2/test_cases/async/async_result.out b/ops/op2/test_cases/async/async_result.out
index 38ff20f..7405fce 100644
--- a/ops/op2/test_cases/async/async_result.out
+++ b/ops/op2/test_cases/async/async_result.out
@@ -4,13 +4,13 @@ pub struct op_async {
 }
 impl deno_core::_ops::Op for op_async {
     const NAME: &'static str = stringify!(op_async);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_async),
         true,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -34,6 +34,12 @@ impl op_async {
             &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
                 as *const deno_core::_ops::OpCtx)
         };
+        if opctx.metrics_enabled() {
+            deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                deno_core::_ops::OpMetricsEvent::Dispatched,
+            );
+        }
         let result = { Self::call() };
         let promise_id = deno_core::_ops::to_i32_option(&args.get(0))
             .unwrap_or_default();
@@ -59,6 +65,9 @@ impl op_async {
             };
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        Self::v8_fn_ptr(info)
+    }
     #[inline(always)]
     pub async fn call() -> std::io::Result<i32> {
         Ok(0)
diff --git a/ops/op2/test_cases/async/async_result_impl.out b/ops/op2/test_cases/async/async_result_impl.out
index 34d7b69..c5286d2 100644
--- a/ops/op2/test_cases/async/async_result_impl.out
+++ b/ops/op2/test_cases/async/async_result_impl.out
@@ -4,13 +4,13 @@ pub struct op_async_result_impl {
 }
 impl deno_core::_ops::Op for op_async_result_impl {
     const NAME: &'static str = stringify!(op_async_result_impl);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_async_result_impl),
         true,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -34,6 +34,12 @@ impl op_async_result_impl {
             &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
                 as *const deno_core::_ops::OpCtx)
         };
+        if opctx.metrics_enabled() {
+            deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                deno_core::_ops::OpMetricsEvent::Dispatched,
+            );
+        }
         let result = {
             let arg0 = args.get(1usize as i32);
             let Some(arg0) = deno_core::_ops::to_i32_option(&arg0) else {
@@ -54,6 +60,12 @@ impl op_async_result_impl {
         let result = match result {
             Ok(result) => result,
             Err(err) => {
+                if opctx.metrics_enabled() {
+                    deno_core::_ops::dispatch_metrics_async(
+                        &opctx,
+                        deno_core::_ops::OpMetricsEvent::Error,
+                    );
+                }
                 let err = err.into();
                 let exception = deno_core::error::to_v8_error(
                     &mut scope,
@@ -88,6 +100,9 @@ impl op_async_result_impl {
             };
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        Self::v8_fn_ptr(info)
+    }
     #[inline(always)]
     pub fn call(x: i32) -> Result<impl Future<Output = std::io::Result<i32>>, AnyError> {
         Ok(async move { Ok(x) })
diff --git a/ops/op2/test_cases/async/async_result_smi.out b/ops/op2/test_cases/async/async_result_smi.out
index fc5b30a..3e88b66 100644
--- a/ops/op2/test_cases/async/async_result_smi.out
+++ b/ops/op2/test_cases/async/async_result_smi.out
@@ -4,13 +4,13 @@ pub struct op_async {
 }
 impl deno_core::_ops::Op for op_async {
     const NAME: &'static str = stringify!(op_async);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_async),
         true,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -34,6 +34,12 @@ impl op_async {
             &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
                 as *const deno_core::_ops::OpCtx)
         };
+        if opctx.metrics_enabled() {
+            deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                deno_core::_ops::OpMetricsEvent::Dispatched,
+            );
+        }
         let result = {
             let arg0 = args.get(1usize as i32);
             let Some(arg0) = deno_core::_ops::to_i32_option(&arg0) else {
@@ -93,6 +99,9 @@ impl op_async {
             };
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        Self::v8_fn_ptr(info)
+    }
     #[inline(always)]
     pub async fn call(rid: ResourceId) -> std::io::Result<ResourceId> {
         Ok(rid as _)
diff --git a/ops/op2/test_cases/async/async_v8_global.out b/ops/op2/test_cases/async/async_v8_global.out
index 56bee2d..46c19e2 100644
--- a/ops/op2/test_cases/async/async_v8_global.out
+++ b/ops/op2/test_cases/async/async_v8_global.out
@@ -4,13 +4,13 @@ pub struct op_async_v8_global {
 }
 impl deno_core::_ops::Op for op_async_v8_global {
     const NAME: &'static str = stringify!(op_async_v8_global);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_async_v8_global),
         true,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -34,6 +34,12 @@ impl op_async_v8_global {
             &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
                 as *const deno_core::_ops::OpCtx)
         };
+        if opctx.metrics_enabled() {
+            deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                deno_core::_ops::OpMetricsEvent::Dispatched,
+            );
+        }
         let result = {
             let arg0 = args.get(1usize as i32);
             let Ok(mut arg0) = deno_core::_ops::v8_try_convert::<
@@ -65,6 +71,9 @@ impl op_async_v8_global {
             deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        Self::v8_fn_ptr(info)
+    }
     #[inline(always)]
     pub async fn call(_s: v8::Global<v8::String>) {}
 }
diff --git a/ops/op2/test_cases/async/async_void.out b/ops/op2/test_cases/async/async_void.out
index a2bde58..e567f0d 100644
--- a/ops/op2/test_cases/async/async_void.out
+++ b/ops/op2/test_cases/async/async_void.out
@@ -4,13 +4,13 @@ pub struct op_async {
 }
 impl deno_core::_ops::Op for op_async {
     const NAME: &'static str = stringify!(op_async);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_async),
         true,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -34,6 +34,12 @@ impl op_async {
             &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
                 as *const deno_core::_ops::OpCtx)
         };
+        if opctx.metrics_enabled() {
+            deno_core::_ops::dispatch_metrics_async(
+                &opctx,
+                deno_core::_ops::OpMetricsEvent::Dispatched,
+            );
+        }
         let result = { Self::call() };
         let promise_id = deno_core::_ops::to_i32_option(&args.get(0))
             .unwrap_or_default();
@@ -47,6 +53,9 @@ impl op_async {
             deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        Self::v8_fn_ptr(info)
+    }
     #[inline(always)]
     pub async fn call() {}
 }
diff --git a/ops/op2/test_cases/sync/add.out b/ops/op2/test_cases/sync/add.out
index 0913716..f145a96 100644
--- a/ops/op2/test_cases/sync/add.out
+++ b/ops/op2/test_cases/sync/add.out
@@ -4,13 +4,12 @@ struct op_add {
 }
 impl deno_core::_ops::Op for op_add {
     const NAME: &'static str = stringify!(op_add);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_add),
         false,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_add {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::Uint32, Type::Uint32, Type::CallbackOptions],
+                CType::Uint32,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_add {
@@ -31,6 +39,31 @@ impl op_add {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: u32,
+        arg1: u32,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> u32 {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0, arg1);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: u32,
@@ -83,6 +116,24 @@ impl op_add {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(a: u32, b: u32) -> u32 {
         a + b
diff --git a/ops/op2/test_cases/sync/add_options.out b/ops/op2/test_cases/sync/add_options.out
index 2f04db9..5795a0d 100644
--- a/ops/op2/test_cases/sync/add_options.out
+++ b/ops/op2/test_cases/sync/add_options.out
@@ -4,13 +4,13 @@ pub struct op_test_add_option {
 }
 impl deno_core::_ops::Op for op_test_add_option {
     const NAME: &'static str = stringify!(op_test_add_option);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_test_add_option),
         false,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -67,6 +67,24 @@ impl op_test_add_option {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call(a: u32, b: Option<u32>) -> u32 {
         a + b.unwrap_or(100)
diff --git a/ops/op2/test_cases/sync/bool.out b/ops/op2/test_cases/sync/bool.out
index fa1e1dc..033eef0 100644
--- a/ops/op2/test_cases/sync/bool.out
+++ b/ops/op2/test_cases/sync/bool.out
@@ -4,13 +4,12 @@ pub struct op_bool {
 }
 impl deno_core::_ops::Op for op_bool {
     const NAME: &'static str = stringify!(op_bool);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_bool),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_bool {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::Bool, Type::CallbackOptions],
+                CType::Bool,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_bool {
@@ -31,6 +39,30 @@ impl op_bool {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: bool,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> bool {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: bool,
@@ -55,6 +87,24 @@ impl op_bool {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call(arg: bool) -> bool {
         arg
diff --git a/ops/op2/test_cases/sync/bool_result.out b/ops/op2/test_cases/sync/bool_result.out
index 8ebff34..79917fd 100644
--- a/ops/op2/test_cases/sync/bool_result.out
+++ b/ops/op2/test_cases/sync/bool_result.out
@@ -4,13 +4,12 @@ pub struct op_bool {
 }
 impl deno_core::_ops::Op for op_bool {
     const NAME: &'static str = stringify!(op_bool);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_bool),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_bool {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::Bool, Type::CallbackOptions],
+                CType::Bool,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_bool {
@@ -31,6 +39,30 @@ impl op_bool {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: bool,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> bool {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0, fast_api_callback_options);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: bool,
@@ -101,6 +133,24 @@ impl op_bool {
             }
         };
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call(arg: bool) -> Result<bool, AnyError> {
         Ok(arg)
diff --git a/ops/op2/test_cases/sync/buffers.out b/ops/op2/test_cases/sync/buffers.out
index 08cc2c9..cae5419 100644
--- a/ops/op2/test_cases/sync/buffers.out
+++ b/ops/op2/test_cases/sync/buffers.out
@@ -4,13 +4,12 @@ struct op_buffers {
 }
 impl deno_core::_ops::Op for op_buffers {
     const NAME: &'static str = stringify!(op_buffers);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_buffers),
         false,
-        false,
-        false,
         4usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -26,6 +25,22 @@ impl deno_core::_ops::Op for op_buffers {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[
+                    Type::V8Value,
+                    Type::TypedArray(CType::Uint8),
+                    Type::TypedArray(CType::Uint8),
+                    Type::TypedArray(CType::Uint8),
+                    Type::TypedArray(CType::Uint8),
+                    Type::CallbackOptions,
+                ],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_buffers {
@@ -37,6 +52,33 @@ impl op_buffers {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
+        arg1: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
+        arg2: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
+        arg3: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0, arg1, arg2, arg3);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
@@ -183,6 +225,24 @@ impl op_buffers {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(_a: &[u8], _b: &mut [u8], _c: *const u8, _d: *mut u8) {}
 }
@@ -193,13 +253,12 @@ struct op_buffers_32 {
 }
 impl deno_core::_ops::Op for op_buffers_32 {
     const NAME: &'static str = stringify!(op_buffers_32);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_buffers_32),
         false,
-        false,
-        false,
         4usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -215,6 +274,22 @@ impl deno_core::_ops::Op for op_buffers_32 {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[
+                    Type::V8Value,
+                    Type::TypedArray(CType::Uint32),
+                    Type::TypedArray(CType::Uint32),
+                    Type::TypedArray(CType::Uint32),
+                    Type::TypedArray(CType::Uint32),
+                    Type::CallbackOptions,
+                ],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_buffers_32 {
@@ -226,6 +301,33 @@ impl op_buffers_32 {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: *mut deno_core::v8::fast_api::FastApiTypedArray<u32>,
+        arg1: *mut deno_core::v8::fast_api::FastApiTypedArray<u32>,
+        arg2: *mut deno_core::v8::fast_api::FastApiTypedArray<u32>,
+        arg3: *mut deno_core::v8::fast_api::FastApiTypedArray<u32>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0, arg1, arg2, arg3);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiTypedArray<u32>,
@@ -372,6 +474,24 @@ impl op_buffers_32 {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(_a: &[u32], _b: &mut [u32], _c: *const u32, _d: *mut u32) {}
 }
@@ -382,13 +502,13 @@ struct op_buffers_option {
 }
 impl deno_core::_ops::Op for op_buffers_option {
     const NAME: &'static str = stringify!(op_buffers_option);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_buffers_option),
         false,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -464,6 +584,24 @@ impl op_buffers_option {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(_a: Option<&[u8]>, _b: Option<JsBuffer>) {}
 }
diff --git a/ops/op2/test_cases/sync/buffers_copy.out b/ops/op2/test_cases/sync/buffers_copy.out
index c6f7307..d6bec48 100644
--- a/ops/op2/test_cases/sync/buffers_copy.out
+++ b/ops/op2/test_cases/sync/buffers_copy.out
@@ -4,13 +4,12 @@ struct op_buffers {
 }
 impl deno_core::_ops::Op for op_buffers {
     const NAME: &'static str = stringify!(op_buffers);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_buffers),
         false,
-        false,
-        false,
         3usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -25,6 +24,21 @@ impl deno_core::_ops::Op for op_buffers {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[
+                    Type::V8Value,
+                    Type::TypedArray(CType::Uint8),
+                    Type::TypedArray(CType::Uint8),
+                    Type::TypedArray(CType::Uint8),
+                    Type::CallbackOptions,
+                ],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_buffers {
@@ -36,6 +50,32 @@ impl op_buffers {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
+        arg1: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
+        arg2: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0, arg1, arg2);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiTypedArray<u8>,
@@ -139,6 +179,24 @@ impl op_buffers {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(_a: Vec<u8>, _b: Box<[u8]>, _c: bytes::Bytes) {}
 }
@@ -149,13 +207,12 @@ struct op_buffers_32 {
 }
 impl deno_core::_ops::Op for op_buffers_32 {
     const NAME: &'static str = stringify!(op_buffers_32);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_buffers_32),
         false,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -169,6 +226,20 @@ impl deno_core::_ops::Op for op_buffers_32 {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[
+                    Type::V8Value,
+                    Type::TypedArray(CType::Uint32),
+                    Type::TypedArray(CType::Uint32),
+                    Type::CallbackOptions,
+                ],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_buffers_32 {
@@ -180,6 +251,31 @@ impl op_buffers_32 {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: *mut deno_core::v8::fast_api::FastApiTypedArray<u32>,
+        arg1: *mut deno_core::v8::fast_api::FastApiTypedArray<u32>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0, arg1);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiTypedArray<u32>,
@@ -256,6 +352,24 @@ impl op_buffers_32 {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(_a: Vec<u32>, _b: Box<[u32]>) {}
 }
diff --git a/ops/op2/test_cases/sync/buffers_out.out b/ops/op2/test_cases/sync/buffers_out.out
index caaaf2c..9c32a2e 100644
--- a/ops/op2/test_cases/sync/buffers_out.out
+++ b/ops/op2/test_cases/sync/buffers_out.out
@@ -4,13 +4,13 @@ struct op_buffers {
 }
 impl deno_core::_ops::Op for op_buffers {
     const NAME: &'static str = stringify!(op_buffers);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_buffers),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -67,6 +67,24 @@ impl op_buffers {
             }
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(buffer: JsBuffer) -> JsBuffer {
         buffer
@@ -79,13 +97,13 @@ struct op_buffers_option {
 }
 impl deno_core::_ops::Op for op_buffers_option {
     const NAME: &'static str = stringify!(op_buffers_option);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_buffers_option),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -149,6 +167,24 @@ impl op_buffers_option {
             }
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(buffer: Option<JsBuffer>) -> Option<JsBuffer> {
         buffer
@@ -161,13 +197,13 @@ struct op_arraybuffers {
 }
 impl deno_core::_ops::Op for op_arraybuffers {
     const NAME: &'static str = stringify!(op_arraybuffers);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_arraybuffers),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -218,6 +254,24 @@ impl op_arraybuffers {
             ),
         )
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(buffer: JsBuffer) -> JsBuffer {
         buffer
diff --git a/ops/op2/test_cases/sync/cfg.out b/ops/op2/test_cases/sync/cfg.out
index 0e14384..7ff72bc 100644
--- a/ops/op2/test_cases/sync/cfg.out
+++ b/ops/op2/test_cases/sync/cfg.out
@@ -6,13 +6,12 @@ pub struct op_maybe_windows {
 }
 impl deno_core::_ops::Op for op_maybe_windows {
     const NAME: &'static str = stringify!(op_maybe_windows);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_maybe_windows),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -22,6 +21,15 @@ impl deno_core::_ops::Op for op_maybe_windows {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_maybe_windows {
@@ -33,6 +41,29 @@ impl op_maybe_windows {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
@@ -44,6 +75,24 @@ impl op_maybe_windows {
         let result = { Self::call() };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     /// This is a doc comment.
     #[cfg(windows)]
@@ -58,13 +107,12 @@ pub struct op_maybe_windows {
 }
 impl deno_core::_ops::Op for op_maybe_windows {
     const NAME: &'static str = stringify!(op_maybe_windows);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_maybe_windows),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -74,6 +122,15 @@ impl deno_core::_ops::Op for op_maybe_windows {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_maybe_windows {
@@ -85,6 +142,29 @@ impl op_maybe_windows {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
@@ -96,6 +176,24 @@ impl op_maybe_windows {
         let result = { Self::call() };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     /// This is a doc comment.
     #[cfg(not(windows))]
diff --git a/ops/op2/test_cases/sync/clippy_allow.out b/ops/op2/test_cases/sync/clippy_allow.out
index 6899ae9..78c3128 100644
--- a/ops/op2/test_cases/sync/clippy_allow.out
+++ b/ops/op2/test_cases/sync/clippy_allow.out
@@ -6,13 +6,12 @@ pub struct op_extra_annotation {
 }
 impl deno_core::_ops::Op for op_extra_annotation {
     const NAME: &'static str = stringify!(op_extra_annotation);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_extra_annotation),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -22,6 +21,15 @@ impl deno_core::_ops::Op for op_extra_annotation {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_extra_annotation {
@@ -33,6 +41,29 @@ impl op_extra_annotation {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
@@ -44,6 +75,24 @@ impl op_extra_annotation {
         let result = { Self::call() };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     /// This is a doc comment.
     #[allow(clippy::some_annotation)]
@@ -56,13 +105,12 @@ pub struct op_clippy_internal {
 }
 impl deno_core::_ops::Op for op_clippy_internal {
     const NAME: &'static str = stringify!(op_clippy_internal);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_clippy_internal),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -72,6 +120,15 @@ impl deno_core::_ops::Op for op_clippy_internal {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_clippy_internal {
@@ -83,6 +140,29 @@ impl op_clippy_internal {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
@@ -94,6 +174,24 @@ impl op_clippy_internal {
         let result = { Self::call() };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call() -> () {
         { #![allow(clippy::await_holding_refcell_ref)] }
diff --git a/ops/op2/test_cases/sync/doc_comment.out b/ops/op2/test_cases/sync/doc_comment.out
index f326403..c1ad588 100644
--- a/ops/op2/test_cases/sync/doc_comment.out
+++ b/ops/op2/test_cases/sync/doc_comment.out
@@ -5,13 +5,12 @@ pub struct op_has_doc_comment {
 }
 impl deno_core::_ops::Op for op_has_doc_comment {
     const NAME: &'static str = stringify!(op_has_doc_comment);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_has_doc_comment),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -21,6 +20,15 @@ impl deno_core::_ops::Op for op_has_doc_comment {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_has_doc_comment {
@@ -32,6 +40,29 @@ impl op_has_doc_comment {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
@@ -43,6 +74,24 @@ impl op_has_doc_comment {
         let result = { Self::call() };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     /// This is a doc comment.
     pub fn call() -> () {}
diff --git a/ops/op2/test_cases/sync/generics.out b/ops/op2/test_cases/sync/generics.out
index d20be2b..cccf9b6 100644
--- a/ops/op2/test_cases/sync/generics.out
+++ b/ops/op2/test_cases/sync/generics.out
@@ -4,13 +4,12 @@ pub struct op_generics<T> {
 }
 impl<T: Trait> deno_core::_ops::Op for op_generics<T> {
     const NAME: &'static str = stringify!(op_generics);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_generics),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl<T: Trait> deno_core::_ops::Op for op_generics<T> {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl<T: Trait> op_generics<T> {
@@ -31,6 +39,29 @@ impl<T: Trait> op_generics<T> {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
@@ -42,6 +73,24 @@ impl<T: Trait> op_generics<T> {
         let result = { Self::call() };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call() {}
 }
@@ -52,13 +101,12 @@ pub struct op_generics_static<T> {
 }
 impl<T: Trait + 'static> deno_core::_ops::Op for op_generics_static<T> {
     const NAME: &'static str = stringify!(op_generics_static);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_generics_static),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -68,6 +116,15 @@ impl<T: Trait + 'static> deno_core::_ops::Op for op_generics_static<T> {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl<T: Trait + 'static> op_generics_static<T> {
@@ -79,6 +136,29 @@ impl<T: Trait + 'static> op_generics_static<T> {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
@@ -90,6 +170,24 @@ impl<T: Trait + 'static> op_generics_static<T> {
         let result = { Self::call() };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call() {}
 }
@@ -100,13 +198,12 @@ pub struct op_generics_static_where<T> {
 }
 impl<T: Trait + 'static> deno_core::_ops::Op for op_generics_static_where<T> {
     const NAME: &'static str = stringify!(op_generics_static_where);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_generics_static_where),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -116,6 +213,15 @@ impl<T: Trait + 'static> deno_core::_ops::Op for op_generics_static_where<T> {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl<T: Trait + 'static> op_generics_static_where<T> {
@@ -127,6 +233,29 @@ impl<T: Trait + 'static> op_generics_static_where<T> {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
         let result = { Self::call() };
         result as _
@@ -138,6 +267,24 @@ impl<T: Trait + 'static> op_generics_static_where<T> {
         let result = { Self::call() };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call()
     where
diff --git a/ops/op2/test_cases/sync/nofast.out b/ops/op2/test_cases/sync/nofast.out
index 96ff079..d28fad0 100644
--- a/ops/op2/test_cases/sync/nofast.out
+++ b/ops/op2/test_cases/sync/nofast.out
@@ -4,13 +4,13 @@ struct op_nofast {
 }
 impl deno_core::_ops::Op for op_nofast {
     const NAME: &'static str = stringify!(op_nofast);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_nofast),
         false,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -62,6 +62,24 @@ impl op_nofast {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(a: u32, b: u32) -> u32 {
         a + b
diff --git a/ops/op2/test_cases/sync/op_state_attr.out b/ops/op2/test_cases/sync/op_state_attr.out
index 8356fb2..cae8f7a 100644
--- a/ops/op2/test_cases/sync/op_state_attr.out
+++ b/ops/op2/test_cases/sync/op_state_attr.out
@@ -4,13 +4,12 @@ struct op_state_rc {
 }
 impl deno_core::_ops::Op for op_state_rc {
     const NAME: &'static str = stringify!(op_state_rc);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_state_rc),
         false,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_state_rc {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_state_rc {
@@ -31,6 +39,29 @@ impl op_state_rc {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, fast_api_callback_options);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
@@ -72,6 +103,24 @@ impl op_state_rc {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(_arg: &Something, _arg_opt: Option<&Something>) {}
 }
diff --git a/ops/op2/test_cases/sync/op_state_rc.out b/ops/op2/test_cases/sync/op_state_rc.out
index acd4bf2..7c9b331 100644
--- a/ops/op2/test_cases/sync/op_state_rc.out
+++ b/ops/op2/test_cases/sync/op_state_rc.out
@@ -4,13 +4,12 @@ struct op_state_rc {
 }
 impl deno_core::_ops::Op for op_state_rc {
     const NAME: &'static str = stringify!(op_state_rc);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_state_rc),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_state_rc {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_state_rc {
@@ -31,6 +39,29 @@ impl op_state_rc {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, fast_api_callback_options);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
@@ -66,6 +97,24 @@ impl op_state_rc {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(_state: Rc<RefCell<OpState>>) {}
 }
diff --git a/ops/op2/test_cases/sync/op_state_ref.out b/ops/op2/test_cases/sync/op_state_ref.out
index c9c98e4..ef0800e 100644
--- a/ops/op2/test_cases/sync/op_state_ref.out
+++ b/ops/op2/test_cases/sync/op_state_ref.out
@@ -4,13 +4,12 @@ struct op_state_ref {
 }
 impl deno_core::_ops::Op for op_state_ref {
     const NAME: &'static str = stringify!(op_state_ref);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_state_ref),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_state_ref {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_state_ref {
@@ -31,6 +39,29 @@ impl op_state_ref {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, fast_api_callback_options);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
@@ -66,6 +97,24 @@ impl op_state_ref {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(_state: &OpState) {}
 }
@@ -76,13 +125,12 @@ struct op_state_mut {
 }
 impl deno_core::_ops::Op for op_state_mut {
     const NAME: &'static str = stringify!(op_state_mut);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_state_mut),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -92,6 +140,15 @@ impl deno_core::_ops::Op for op_state_mut {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_state_mut {
@@ -103,6 +160,29 @@ impl op_state_mut {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, fast_api_callback_options);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
@@ -138,6 +218,24 @@ impl op_state_mut {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(_state: &mut OpState) {}
 }
@@ -148,13 +246,13 @@ struct op_state_and_v8 {
 }
 impl deno_core::_ops::Op for op_state_and_v8 {
     const NAME: &'static str = stringify!(op_state_and_v8);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_state_and_v8),
         false,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -201,6 +299,24 @@ impl op_state_and_v8 {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(_state: &mut OpState, _callback: v8::Global<v8::Function>) {}
 }
diff --git a/ops/op2/test_cases/sync/result_external.out b/ops/op2/test_cases/sync/result_external.out
index c18f276..53e2c9d 100644
--- a/ops/op2/test_cases/sync/result_external.out
+++ b/ops/op2/test_cases/sync/result_external.out
@@ -4,13 +4,12 @@ pub struct op_external_with_result {
 }
 impl deno_core::_ops::Op for op_external_with_result {
     const NAME: &'static str = stringify!(op_external_with_result);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_external_with_result),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_external_with_result {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Pointer,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_external_with_result {
@@ -31,6 +39,29 @@ impl op_external_with_result {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> *mut ::std::ffi::c_void {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, fast_api_callback_options);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
@@ -99,6 +130,24 @@ impl op_external_with_result {
             }
         };
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call() -> Result<*mut std::ffi::c_void, AnyError> {
         Ok(0 as _)
diff --git a/ops/op2/test_cases/sync/result_primitive.out b/ops/op2/test_cases/sync/result_primitive.out
index e8e5270..0220231 100644
--- a/ops/op2/test_cases/sync/result_primitive.out
+++ b/ops/op2/test_cases/sync/result_primitive.out
@@ -4,13 +4,12 @@ pub struct op_u32_with_result {
 }
 impl deno_core::_ops::Op for op_u32_with_result {
     const NAME: &'static str = stringify!(op_u32_with_result);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_u32_with_result),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_u32_with_result {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Uint32,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_u32_with_result {
@@ -31,6 +39,29 @@ impl op_u32_with_result {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> u32 {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, fast_api_callback_options);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
@@ -99,6 +130,24 @@ impl op_u32_with_result {
             }
         };
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call() -> Result<u32, AnyError> {
         Ok(0)
diff --git a/ops/op2/test_cases/sync/result_scope.out b/ops/op2/test_cases/sync/result_scope.out
index 36fa87c..3938217 100644
--- a/ops/op2/test_cases/sync/result_scope.out
+++ b/ops/op2/test_cases/sync/result_scope.out
@@ -4,13 +4,13 @@ pub struct op_void_with_result {
 }
 impl deno_core::_ops::Op for op_void_with_result {
     const NAME: &'static str = stringify!(op_void_with_result);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_void_with_result),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -52,6 +52,24 @@ impl op_void_with_result {
             }
         };
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call(_scope: &mut v8::HandleScope) -> Result<(), AnyError> {
         Ok(())
diff --git a/ops/op2/test_cases/sync/result_void.out b/ops/op2/test_cases/sync/result_void.out
index 3ff0f62..a6e25d6 100644
--- a/ops/op2/test_cases/sync/result_void.out
+++ b/ops/op2/test_cases/sync/result_void.out
@@ -4,13 +4,12 @@ pub struct op_void_with_result {
 }
 impl deno_core::_ops::Op for op_void_with_result {
     const NAME: &'static str = stringify!(op_void_with_result);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_void_with_result),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_void_with_result {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_void_with_result {
@@ -31,6 +39,29 @@ impl op_void_with_result {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, fast_api_callback_options);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
@@ -99,6 +130,24 @@ impl op_void_with_result {
             }
         };
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call() -> Result<(), AnyError> {
         Ok(())
diff --git a/ops/op2/test_cases/sync/serde_v8.out b/ops/op2/test_cases/sync/serde_v8.out
index b96e0e4..9bdfef6 100644
--- a/ops/op2/test_cases/sync/serde_v8.out
+++ b/ops/op2/test_cases/sync/serde_v8.out
@@ -4,13 +4,13 @@ pub struct op_serde_v8 {
 }
 impl deno_core::_ops::Op for op_serde_v8 {
     const NAME: &'static str = stringify!(op_serde_v8);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_serde_v8),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -67,6 +67,24 @@ impl op_serde_v8 {
             }
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call(_input: Input) -> Output {
         Output {}
diff --git a/ops/op2/test_cases/sync/smi.out b/ops/op2/test_cases/sync/smi.out
index 9529eaa..f311bbf 100644
--- a/ops/op2/test_cases/sync/smi.out
+++ b/ops/op2/test_cases/sync/smi.out
@@ -4,13 +4,12 @@ struct op_smi_unsigned_return {
 }
 impl deno_core::_ops::Op for op_smi_unsigned_return {
     const NAME: &'static str = stringify!(op_smi_unsigned_return);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_smi_unsigned_return),
         false,
-        false,
-        false,
         4usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,22 @@ impl deno_core::_ops::Op for op_smi_unsigned_return {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[
+                    Type::V8Value,
+                    Type::Int32,
+                    Type::Int32,
+                    Type::Int32,
+                    Type::Int32,
+                    Type::CallbackOptions,
+                ],
+                CType::Int32,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_smi_unsigned_return {
@@ -31,6 +46,33 @@ impl op_smi_unsigned_return {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: i32,
+        arg1: i32,
+        arg2: i32,
+        arg3: i32,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> i32 {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0, arg1, arg2, arg3);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: i32,
@@ -121,6 +163,24 @@ impl op_smi_unsigned_return {
             &mut rv,
         )
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(a: Int16, b: Int32, c: Uint16, d: Uint32) -> Uint32 {
         a as Uint32 + b as Uint32 + c as Uint32 + d as Uint32
@@ -133,13 +193,12 @@ struct op_smi_signed_return {
 }
 impl deno_core::_ops::Op for op_smi_signed_return {
     const NAME: &'static str = stringify!(op_smi_signed_return);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_smi_signed_return),
         false,
-        false,
-        false,
         4usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -149,6 +208,22 @@ impl deno_core::_ops::Op for op_smi_signed_return {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[
+                    Type::V8Value,
+                    Type::Int32,
+                    Type::Int32,
+                    Type::Int32,
+                    Type::Int32,
+                    Type::CallbackOptions,
+                ],
+                CType::Int32,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_smi_signed_return {
@@ -160,6 +235,33 @@ impl op_smi_signed_return {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: i32,
+        arg1: i32,
+        arg2: i32,
+        arg3: i32,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> i32 {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0, arg1, arg2, arg3);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: i32,
@@ -250,6 +352,24 @@ impl op_smi_signed_return {
             &mut rv,
         )
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(a: Int16, b: Int32, c: Uint16, d: Uint32) -> Int32 {
         a as Int32 + b as Int32 + c as Int32 + d as Int32
@@ -262,13 +382,13 @@ struct op_smi_option {
 }
 impl deno_core::_ops::Op for op_smi_option {
     const NAME: &'static str = stringify!(op_smi_option);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_smi_option),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -311,6 +431,24 @@ impl op_smi_option {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(a: Option<Uint32>) -> Option<Uint32> {
         a
diff --git a/ops/op2/test_cases/sync/string_cow.out b/ops/op2/test_cases/sync/string_cow.out
index 0e25bba..ae84b09 100644
--- a/ops/op2/test_cases/sync/string_cow.out
+++ b/ops/op2/test_cases/sync/string_cow.out
@@ -4,13 +4,12 @@ struct op_string_cow {
 }
 impl deno_core::_ops::Op for op_string_cow {
     const NAME: &'static str = stringify!(op_string_cow);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_string_cow),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_string_cow {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::SeqOneByteString, Type::CallbackOptions],
+                CType::Uint32,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_string_cow {
@@ -31,6 +39,30 @@ impl op_string_cow {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> u32 {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
@@ -69,6 +101,24 @@ impl op_string_cow {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(s: Cow<str>) -> u32 {
         s.len() as _
diff --git a/ops/op2/test_cases/sync/string_onebyte.out b/ops/op2/test_cases/sync/string_onebyte.out
index 1bc5d96..74ca74a 100644
--- a/ops/op2/test_cases/sync/string_onebyte.out
+++ b/ops/op2/test_cases/sync/string_onebyte.out
@@ -4,13 +4,12 @@ struct op_string_onebyte {
 }
 impl deno_core::_ops::Op for op_string_onebyte {
     const NAME: &'static str = stringify!(op_string_onebyte);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_string_onebyte),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_string_onebyte {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::SeqOneByteString, Type::CallbackOptions],
+                CType::Uint32,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_string_onebyte {
@@ -31,6 +39,30 @@ impl op_string_onebyte {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> u32 {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
@@ -74,6 +106,24 @@ impl op_string_onebyte {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(s: Cow<[u8]>) -> u32 {
         s.len() as _
diff --git a/ops/op2/test_cases/sync/string_option_return.out b/ops/op2/test_cases/sync/string_option_return.out
index f69234a..b1af1f4 100644
--- a/ops/op2/test_cases/sync/string_option_return.out
+++ b/ops/op2/test_cases/sync/string_option_return.out
@@ -4,13 +4,13 @@ pub struct op_string_return {
 }
 impl deno_core::_ops::Op for op_string_return {
     const NAME: &'static str = stringify!(op_string_return);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_string_return),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -53,6 +53,24 @@ impl op_string_return {
             }
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call(s: Option<String>) -> Option<String> {
         s
diff --git a/ops/op2/test_cases/sync/string_owned.out b/ops/op2/test_cases/sync/string_owned.out
index ea265df..248c341 100644
--- a/ops/op2/test_cases/sync/string_owned.out
+++ b/ops/op2/test_cases/sync/string_owned.out
@@ -4,13 +4,12 @@ struct op_string_owned {
 }
 impl deno_core::_ops::Op for op_string_owned {
     const NAME: &'static str = stringify!(op_string_owned);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_string_owned),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_string_owned {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::SeqOneByteString, Type::CallbackOptions],
+                CType::Uint32,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_string_owned {
@@ -31,6 +39,30 @@ impl op_string_owned {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> u32 {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
@@ -60,6 +92,24 @@ impl op_string_owned {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(s: String) -> u32 {
         s.len() as _
diff --git a/ops/op2/test_cases/sync/string_ref.out b/ops/op2/test_cases/sync/string_ref.out
index 529224d..0ae375a 100644
--- a/ops/op2/test_cases/sync/string_ref.out
+++ b/ops/op2/test_cases/sync/string_ref.out
@@ -4,13 +4,12 @@ struct op_string_owned {
 }
 impl deno_core::_ops::Op for op_string_owned {
     const NAME: &'static str = stringify!(op_string_owned);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_string_owned),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_string_owned {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::SeqOneByteString, Type::CallbackOptions],
+                CType::Uint32,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_string_owned {
@@ -31,6 +39,30 @@ impl op_string_owned {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> u32 {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
@@ -69,6 +101,24 @@ impl op_string_owned {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call(s: &str) -> u32 {
         s.len() as _
diff --git a/ops/op2/test_cases/sync/string_return.out b/ops/op2/test_cases/sync/string_return.out
index 40d3564..a6c1d33 100644
--- a/ops/op2/test_cases/sync/string_return.out
+++ b/ops/op2/test_cases/sync/string_return.out
@@ -4,13 +4,13 @@ pub struct op_string_return {
 }
 impl deno_core::_ops::Op for op_string_return {
     const NAME: &'static str = stringify!(op_string_return);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_string_return),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -42,6 +42,24 @@ impl op_string_return {
             }
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call() -> String {
         "".into()
@@ -54,13 +72,13 @@ pub struct op_string_return_ref {
 }
 impl deno_core::_ops::Op for op_string_return_ref {
     const NAME: &'static str = stringify!(op_string_return_ref);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_string_return_ref),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -92,6 +110,24 @@ impl op_string_return_ref {
             }
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call() -> &'static str {
         ""
@@ -104,13 +140,13 @@ pub struct op_string_return_cow {
 }
 impl deno_core::_ops::Op for op_string_return_cow {
     const NAME: &'static str = stringify!(op_string_return_cow);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_string_return_cow),
         false,
-        false,
-        false,
         0usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -142,6 +178,24 @@ impl op_string_return_cow {
             }
         }
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call<'a>() -> Cow<'a, str> {
         "".into()
diff --git a/ops/op2/test_cases/sync/v8_global.out b/ops/op2/test_cases/sync/v8_global.out
index e8ad497..ffbc737 100644
--- a/ops/op2/test_cases/sync/v8_global.out
+++ b/ops/op2/test_cases/sync/v8_global.out
@@ -4,13 +4,13 @@ pub struct op_global {
 }
 impl deno_core::_ops::Op for op_global {
     const NAME: &'static str = stringify!(op_global);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_global),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -51,6 +51,24 @@ impl op_global {
         };
         rv.set(deno_core::_ops::RustToV8::to_v8(result, &mut scope))
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call(g: v8::Global<v8::String>) -> v8::Global<v8::String> {
         g
diff --git a/ops/op2/test_cases/sync/v8_handlescope.out b/ops/op2/test_cases/sync/v8_handlescope.out
index 2ee7d81..7676e12 100644
--- a/ops/op2/test_cases/sync/v8_handlescope.out
+++ b/ops/op2/test_cases/sync/v8_handlescope.out
@@ -4,13 +4,13 @@ struct op_handlescope {
 }
 impl deno_core::_ops::Op for op_handlescope {
     const NAME: &'static str = stringify!(op_handlescope);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_handlescope),
         false,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -51,6 +51,24 @@ impl op_handlescope {
         };
         rv.set(deno_core::_ops::RustToV8NoScope::to_v8(result))
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call<'a>(
         _scope: &v8::HandleScope<'a>,
diff --git a/ops/op2/test_cases/sync/v8_lifetime.out b/ops/op2/test_cases/sync/v8_lifetime.out
index 010be55..8214905 100644
--- a/ops/op2/test_cases/sync/v8_lifetime.out
+++ b/ops/op2/test_cases/sync/v8_lifetime.out
@@ -4,13 +4,13 @@ pub struct op_v8_lifetime {
 }
 impl deno_core::_ops::Op for op_v8_lifetime {
     const NAME: &'static str = stringify!(op_v8_lifetime);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_v8_lifetime),
         false,
-        false,
-        false,
         1usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -50,6 +50,24 @@ impl op_v8_lifetime {
         };
         rv.set(deno_core::_ops::RustToV8NoScope::to_v8(result))
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call<'s>(_s: v8::Local<'s, v8::String>) -> v8::Local<'s, v8::String> {
         unimplemented!()
diff --git a/ops/op2/test_cases/sync/v8_ref_option.out b/ops/op2/test_cases/sync/v8_ref_option.out
index 12d1bd8..8a4bd6c 100644
--- a/ops/op2/test_cases/sync/v8_ref_option.out
+++ b/ops/op2/test_cases/sync/v8_ref_option.out
@@ -4,13 +4,12 @@ pub struct op_v8_lifetime {
 }
 impl deno_core::_ops::Op for op_v8_lifetime {
     const NAME: &'static str = stringify!(op_v8_lifetime);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_v8_lifetime),
         false,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
         Some({
             use deno_core::v8::fast_api::Type;
             use deno_core::v8::fast_api::CType;
@@ -20,6 +19,15 @@ impl deno_core::_ops::Op for op_v8_lifetime {
                 Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
             )
         }),
+        Some({
+            use deno_core::v8::fast_api::Type;
+            use deno_core::v8::fast_api::CType;
+            deno_core::v8::fast_api::FastFunction::new_with_bigint(
+                &[Type::V8Value, Type::V8Value, Type::V8Value, Type::CallbackOptions],
+                CType::Void,
+                Self::v8_fn_ptr_fast_metrics as *const ::std::ffi::c_void,
+            )
+        }),
     );
 }
 impl op_v8_lifetime {
@@ -31,6 +39,31 @@ impl op_v8_lifetime {
         <Self as deno_core::_ops::Op>::DECL
     }
     #[allow(clippy::too_many_arguments)]
+    fn v8_fn_ptr_fast_metrics(
+        this: deno_core::v8::Local<deno_core::v8::Object>,
+        arg0: deno_core::v8::Local<deno_core::v8::Value>,
+        arg1: deno_core::v8::Local<deno_core::v8::Value>,
+        fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
+    ) -> () {
+        let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<
+                deno_core::v8::External,
+            >::cast(unsafe { fast_api_callback_options.data.data })
+                .value() as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        let res = Self::v8_fn_ptr_fast(this, arg0, arg1, fast_api_callback_options);
+        deno_core::_ops::dispatch_metrics_fast(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+        res
+    }
+    #[allow(clippy::too_many_arguments)]
     fn v8_fn_ptr_fast(
         _: deno_core::v8::Local<deno_core::v8::Object>,
         arg0: deno_core::v8::Local<deno_core::v8::Value>,
@@ -113,6 +146,24 @@ impl op_v8_lifetime {
         };
         deno_core::_ops::RustToV8RetVal::to_v8_rv(result, &mut rv)
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     pub fn call<'s>(_s: Option<&v8::String>, _s2: Option<&v8::String>) {}
 }
diff --git a/ops/op2/test_cases/sync/v8_string.out b/ops/op2/test_cases/sync/v8_string.out
index f7181a5..042f818 100644
--- a/ops/op2/test_cases/sync/v8_string.out
+++ b/ops/op2/test_cases/sync/v8_string.out
@@ -4,13 +4,13 @@ struct op_v8_string {
 }
 impl deno_core::_ops::Op for op_v8_string {
     const NAME: &'static str = stringify!(op_v8_string);
-    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal(
+    const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl::new_internal_op2(
         stringify!(op_v8_string),
         false,
-        false,
-        false,
         2usize as u8,
         Self::v8_fn_ptr as _,
+        Self::v8_fn_ptr_metrics as _,
+        None,
         None,
     );
 }
@@ -66,6 +66,24 @@ impl op_v8_string {
         };
         rv.set(deno_core::_ops::RustToV8NoScope::to_v8(result))
     }
+    extern "C" fn v8_fn_ptr_metrics(info: *const deno_core::v8::FunctionCallbackInfo) {
+        let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
+            &*info
+        });
+        let opctx = unsafe {
+            &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
+                as *const deno_core::_ops::OpCtx)
+        };
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Dispatched,
+        );
+        Self::v8_fn_ptr(info);
+        deno_core::_ops::dispatch_metrics_slow(
+            &opctx,
+            deno_core::_ops::OpMetricsEvent::Completed,
+        );
+    }
     #[inline(always)]
     fn call<'a>(
         _str1: &v8::String,