offer function-level control over tracing (#5194)

* wiggle: fix compilation with async functions when tracing is off

Fixes #5202

* switch tracing config from a boolean to a struct

This will enable more complex tracing rules in the future

* rename AsyncConfField to FunctionField

It is going to be reused for cases other than just async functions

* add support for disabling tracing per-function

This adds a `disable_for` syntax after the `tracing` boolean.  For
example:

```
wiggle::from_witx!(
    tracing: true disable_for {
        module1::foo,
        module2::{bar, baz},
    }
)
```
This commit is contained in:
Joe Shaw
2022-11-05 14:31:09 -04:00
committed by GitHub
parent fba2287c54
commit 1ddf03aaa1
5 changed files with 99 additions and 19 deletions

View File

@@ -1,4 +1,4 @@
use crate::config::{AsyncConf, ErrorConf}; use crate::config::{AsyncConf, ErrorConf, TracingConf};
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
@@ -15,7 +15,7 @@ pub struct CodegenSettings {
// Disabling this feature makes it possible to remove all of the tracing // Disabling this feature makes it possible to remove all of the tracing
// code emitted in the Wiggle-generated code; this can be helpful while // code emitted in the Wiggle-generated code; this can be helpful while
// inspecting the code (e.g., with `cargo expand`). // inspecting the code (e.g., with `cargo expand`).
pub tracing: bool, pub tracing: TracingConf,
} }
impl CodegenSettings { impl CodegenSettings {
pub fn new( pub fn new(
@@ -23,14 +23,14 @@ impl CodegenSettings {
async_: &AsyncConf, async_: &AsyncConf,
doc: &Document, doc: &Document,
wasmtime: bool, wasmtime: bool,
tracing: bool, tracing: &TracingConf,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let errors = ErrorTransform::new(error_conf, doc)?; let errors = ErrorTransform::new(error_conf, doc)?;
Ok(Self { Ok(Self {
errors, errors,
async_: async_.clone(), async_: async_.clone(),
wasmtime, wasmtime,
tracing, tracing: tracing.clone(),
}) })
} }
pub fn get_async(&self, module: &Module, func: &InterfaceFunc) -> Asyncness { pub fn get_async(&self, module: &Module, func: &InterfaceFunc) -> Asyncness {

View File

@@ -15,7 +15,7 @@ pub struct Config {
pub errors: ErrorConf, pub errors: ErrorConf,
pub async_: AsyncConf, pub async_: AsyncConf,
pub wasmtime: bool, pub wasmtime: bool,
pub tracing: bool, pub tracing: TracingConf,
} }
mod kw { mod kw {
@@ -26,6 +26,7 @@ mod kw {
syn::custom_keyword!(target); syn::custom_keyword!(target);
syn::custom_keyword!(wasmtime); syn::custom_keyword!(wasmtime);
syn::custom_keyword!(tracing); syn::custom_keyword!(tracing);
syn::custom_keyword!(disable_for);
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -34,7 +35,7 @@ pub enum ConfigField {
Error(ErrorConf), Error(ErrorConf),
Async(AsyncConf), Async(AsyncConf),
Wasmtime(bool), Wasmtime(bool),
Tracing(bool), Tracing(TracingConf),
} }
impl Parse for ConfigField { impl Parse for ConfigField {
@@ -73,7 +74,7 @@ impl Parse for ConfigField {
} else if lookahead.peek(kw::tracing) { } else if lookahead.peek(kw::tracing) {
input.parse::<kw::tracing>()?; input.parse::<kw::tracing>()?;
input.parse::<Token![:]>()?; input.parse::<Token![:]>()?;
Ok(ConfigField::Tracing(input.parse::<syn::LitBool>()?.value)) Ok(ConfigField::Tracing(input.parse()?))
} else { } else {
Err(lookahead.error()) Err(lookahead.error())
} }
@@ -128,7 +129,7 @@ impl Config {
errors: errors.take().unwrap_or_default(), errors: errors.take().unwrap_or_default(),
async_: async_.take().unwrap_or_default(), async_: async_.take().unwrap_or_default(),
wasmtime: wasmtime.unwrap_or(true), wasmtime: wasmtime.unwrap_or(true),
tracing: tracing.unwrap_or(true), tracing: tracing.unwrap_or_default(),
}) })
} }
@@ -409,7 +410,7 @@ impl Parse for AsyncFunctions {
let lookahead = input.lookahead1(); let lookahead = input.lookahead1();
if lookahead.peek(syn::token::Brace) { if lookahead.peek(syn::token::Brace) {
let _ = braced!(content in input); let _ = braced!(content in input);
let items: Punctuated<AsyncConfField, Token![,]> = let items: Punctuated<FunctionField, Token![,]> =
content.parse_terminated(Parse::parse)?; content.parse_terminated(Parse::parse)?;
let mut functions: HashMap<String, Vec<String>> = HashMap::new(); let mut functions: HashMap<String, Vec<String>> = HashMap::new();
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
@@ -437,13 +438,13 @@ impl Parse for AsyncFunctions {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct AsyncConfField { pub struct FunctionField {
pub module_name: Ident, pub module_name: Ident,
pub function_names: Vec<Ident>, pub function_names: Vec<Ident>,
pub err_loc: Span, pub err_loc: Span,
} }
impl Parse for AsyncConfField { impl Parse for FunctionField {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
let err_loc = input.span(); let err_loc = input.span();
let module_name = input.parse::<Ident>()?; let module_name = input.parse::<Ident>()?;
@@ -454,14 +455,14 @@ impl Parse for AsyncConfField {
let _ = braced!(content in input); let _ = braced!(content in input);
let function_names: Punctuated<Ident, Token![,]> = let function_names: Punctuated<Ident, Token![,]> =
content.parse_terminated(Parse::parse)?; content.parse_terminated(Parse::parse)?;
Ok(AsyncConfField { Ok(FunctionField {
module_name, module_name,
function_names: function_names.iter().cloned().collect(), function_names: function_names.iter().cloned().collect(),
err_loc, err_loc,
}) })
} else if lookahead.peek(Ident) { } else if lookahead.peek(Ident) {
let name = input.parse()?; let name = input.parse()?;
Ok(AsyncConfField { Ok(FunctionField {
module_name, module_name,
function_names: vec![name], function_names: vec![name],
err_loc, err_loc,
@@ -565,3 +566,70 @@ impl Parse for WasmtimeConfigField {
} }
} }
} }
#[derive(Clone, Debug)]
pub struct TracingConf {
enabled: bool,
excluded_functions: HashMap<String, Vec<String>>,
}
impl TracingConf {
pub fn enabled_for(&self, module: &str, function: &str) -> bool {
if !self.enabled {
return false;
}
self.excluded_functions
.get(module)
.and_then(|fs| fs.iter().find(|f| *f == function))
.is_none()
}
}
impl Default for TracingConf {
fn default() -> Self {
Self {
enabled: true,
excluded_functions: HashMap::new(),
}
}
}
impl Parse for TracingConf {
fn parse(input: ParseStream) -> Result<Self> {
let enabled = input.parse::<syn::LitBool>()?.value;
let lookahead = input.lookahead1();
if lookahead.peek(kw::disable_for) {
input.parse::<kw::disable_for>()?;
let content;
let _ = braced!(content in input);
let items: Punctuated<FunctionField, Token![,]> =
content.parse_terminated(Parse::parse)?;
let mut functions: HashMap<String, Vec<String>> = HashMap::new();
use std::collections::hash_map::Entry;
for i in items {
let function_names = i
.function_names
.iter()
.map(|i| i.to_string())
.collect::<Vec<String>>();
match functions.entry(i.module_name.to_string()) {
Entry::Occupied(o) => o.into_mut().extend(function_names),
Entry::Vacant(v) => {
v.insert(function_names);
}
}
}
Ok(TracingConf {
enabled,
excluded_functions: functions,
})
} else {
Ok(TracingConf {
enabled,
excluded_functions: HashMap::new(),
})
}
}
}

View File

@@ -84,7 +84,7 @@ fn _define_func(
); );
); );
if settings.get_async(&module, &func).is_sync() { if settings.get_async(&module, &func).is_sync() {
let traced_body = if settings.tracing { let traced_body = if settings.tracing.enabled_for(&mod_name, &func_name) {
quote!( quote!(
#mk_span #mk_span
_span.in_scope(|| { _span.in_scope(|| {
@@ -109,7 +109,7 @@ fn _define_func(
bounds, bounds,
) )
} else { } else {
let traced_body = if settings.tracing { let traced_body = if settings.tracing.enabled_for(&mod_name, &func_name) {
quote!( quote!(
use #rt::tracing::Instrument as _; use #rt::tracing::Instrument as _;
#mk_span #mk_span
@@ -261,7 +261,12 @@ impl witx::Bindgen for Rust<'_> {
args.push(quote!(#name)); args.push(quote!(#name));
} }
} }
if self.settings.tracing && func.params.len() > 0 { if self
.settings
.tracing
.enabled_for(self.module.name.as_str(), self.funcname)
&& func.params.len() > 0
{
let args = func let args = func
.params .params
.iter() .iter()
@@ -290,7 +295,11 @@ impl witx::Bindgen for Rust<'_> {
let ret = #trait_name::#ident(ctx, #(#args),*).await; let ret = #trait_name::#ident(ctx, #(#args),*).await;
}) })
}; };
if self.settings.tracing { if self
.settings
.tracing
.enabled_for(self.module.name.as_str(), self.funcname)
{
self.src.extend(quote! { self.src.extend(quote! {
#rt::tracing::event!( #rt::tracing::event!(
#rt::tracing::Level::TRACE, #rt::tracing::Level::TRACE,

View File

@@ -153,7 +153,7 @@ pub fn from_witx(args: TokenStream) -> TokenStream {
&config.async_, &config.async_,
&doc, &doc,
config.wasmtime, config.wasmtime,
config.tracing, &config.tracing,
) )
.expect("validating codegen settings"); .expect("validating codegen settings");
@@ -195,7 +195,7 @@ pub fn wasmtime_integration(args: TokenStream) -> TokenStream {
&config.c.async_, &config.c.async_,
&doc, &doc,
true, true,
config.c.tracing, &config.c.tracing,
) )
.expect("validating codegen settings"); .expect("validating codegen settings");

View File

@@ -15,6 +15,9 @@ pub enum RichError {
// Define an errno with variants corresponding to RichError. Use it in a // Define an errno with variants corresponding to RichError. Use it in a
// trivial function. // trivial function.
wiggle::from_witx!({ wiggle::from_witx!({
tracing: true disable_for {
one_error_conversion::foo,
},
witx_literal: " witx_literal: "
(typename $errno (enum (@witx tag u8) $ok $invalid_arg $picket_line)) (typename $errno (enum (@witx tag u8) $ok $invalid_arg $picket_line))
(typename $s (record (field $f1 (@witx usize)) (field $f2 (@witx pointer u8)))) (typename $s (record (field $f1 (@witx usize)) (field $f2 (@witx pointer u8))))