From 859d88b918107451af2854a4733aee67e9cba952 Mon Sep 17 00:00:00 2001 From: Dan Berkowitz Date: Mon, 2 Nov 2026 14:02:09 -0500 Subject: [PATCH] Only format probe strings when the probe is enabled The generated pipeline and p4rs::table::key_matches built their DTrace probe strings (header dumps, action arguments, table-miss keys) before calling the probe macro, so every packet paid for string formatting even with no probes enabled. Build the strings inside the probe closures, which usdt only calls when the probe is enabled. usdt accepts any AsRef for string arguments, so the closures can return an owned String. Probe output is unchanged. --- codegen/rust/src/control.rs | 5 +---- codegen/rust/src/pipeline.rs | 20 ++++++++------------ lang/p4rs/src/table.rs | 22 ++++++++++------------ 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/codegen/rust/src/control.rs b/codegen/rust/src/control.rs index 6c1bbb36..6682219e 100644 --- a/codegen/rust/src/control.rs +++ b/codegen/rust/src/control.rs @@ -227,10 +227,7 @@ impl<'a> ControlGenerator<'a> { .collect(); let dump = quote! { - // TODO find a way to only allocate the string when probe is active. - // Cannot simply return reference to string created within probe. - let dump = format!(#dump_fmt, #(#dump_args,)*); - softnpu_provider::action!(|| (&dump)); + softnpu_provider::action!(|| format!(#dump_fmt, #(#dump_args,)*)); }; for arg in &action.parameters { diff --git a/codegen/rust/src/pipeline.rs b/codegen/rust/src/pipeline.rs index 4bca914e..82dc0225 100644 --- a/codegen/rust/src/pipeline.rs +++ b/codegen/rust/src/pipeline.rs @@ -386,8 +386,7 @@ impl<'a> PipelineGenerator<'a> { softnpu_provider::parser_dropped!(||()); return Vec::new(); } - let dump = format!("\n{}", parsed.dump()); - softnpu_provider::parser_accepted!(||(&dump)); + softnpu_provider::parser_accepted!(|| format!("\n{}", parsed.dump())); // // Calculate parsed header size @@ -412,19 +411,18 @@ impl<'a> PipelineGenerator<'a> { #egress_ports - let dump = format!( + let dump = || format!( "{}\negress:\n{}", parsed.dump(), #egress_meta_var.dump(), ); if ports.is_empty() { - softnpu_provider::ingress_dropped!(||(&dump)); + softnpu_provider::ingress_dropped!(|| dump()); return Vec::new(); } - let dump = format!("\n{dump}"); - softnpu_provider::ingress_accepted!(||(&dump)); + softnpu_provider::ingress_accepted!(|| format!("\n{}", dump())); // // Run output of ingress block through egress block on each @@ -473,8 +471,7 @@ impl<'a> PipelineGenerator<'a> { softnpu_provider::parser_dropped!(||()); return Vec::new(); } - let dump = format!("\n{}", parsed.dump()); - softnpu_provider::parser_accepted!(||(&dump)); + softnpu_provider::parser_accepted!(|| format!("\n{}", parsed.dump())); // // Calculate parsed header size @@ -499,19 +496,18 @@ impl<'a> PipelineGenerator<'a> { #egress_ports - let dump = format!( + let dump = || format!( "{}\negress:\n{}", parsed.dump(), #egress_meta_var.dump(), ); if ports.is_empty() { - softnpu_provider::ingress_dropped!(||(&dump)); + softnpu_provider::ingress_dropped!(|| dump()); return Vec::new(); } - let dump = format!("\n{dump}"); - softnpu_provider::ingress_accepted!(||(&dump)); + softnpu_provider::ingress_accepted!(|| format!("\n{}", dump())); // // Run output of ingress block through egress block on each diff --git a/lang/p4rs/src/table.rs b/lang/p4rs/src/table.rs index 535062e9..7962a4fb 100644 --- a/lang/p4rs/src/table.rs +++ b/lang/p4rs/src/table.rs @@ -220,19 +220,20 @@ pub fn key_matches(selector: &BigUint, key: &Key) -> bool { Key::Exact(x) => { let hit = selector == &x.value; if !hit { - let dump = format!("{:x} != {:x}", selector, x.value); - crate::p4rs_provider::match_miss!(|| &dump); + crate::p4rs_provider::match_miss!(|| format!( + "{:x} != {:x}", + selector, x.value + )); } hit } Key::Range(begin, end) => { let hit = selector >= &begin.value && selector <= &end.value; if !hit { - let dump = format!( + crate::p4rs_provider::match_miss!(|| format!( "begin={} end={} sel={}", begin.value, end.value, selector - ); - crate::p4rs_provider::match_miss!(|| &dump); + )); } hit } @@ -256,7 +257,7 @@ pub fn key_matches(selector: &BigUint, key: &Key) -> bool { let selector_v6 = selector.to_u128().unwrap(); let hit = selector_v6 & mask == key & mask; if !hit { - let dump = format!( + crate::p4rs_provider::match_miss!(|| format!( "{:x} & {:x} == {:x} & {:x} | {:x} == {:x}", selector_v6, mask, @@ -264,9 +265,7 @@ pub fn key_matches(selector: &BigUint, key: &Key) -> bool { mask, selector_v6 & mask, key & mask - ); - //println!("{}", dump); - crate::p4rs_provider::match_miss!(|| &dump); + )); } hit } @@ -283,7 +282,7 @@ pub fn key_matches(selector: &BigUint, key: &Key) -> bool { let selector_v4: u32 = selector.to_u32().unwrap(); let hit = selector_v4 & mask == key & mask; if !hit { - let dump = format!( + crate::p4rs_provider::match_miss!(|| format!( "{:x} & {:x} == {:x} & {:x} | {:x} = {:x}", selector_v4, mask, @@ -291,8 +290,7 @@ pub fn key_matches(selector: &BigUint, key: &Key) -> bool { mask, selector_v4 & mask, key & mask - ); - crate::p4rs_provider::match_miss!(|| &dump); + )); } hit }