Repository: bytecodealliance/wasmtime
Pull request #6209 (2023-04-13 21:20:06)

wasmtime component bindgen: when tracing is enabled, emit an event for args & results

This is consistient with what wiggle does (see
https://github.com/bytecodealliance/wasmtime/blob/main/crates/wiggle/generate/src/funcs.rs#L266), with the exceptions that
1. wiggle has a facility for disabling tracing on a per-function basis, a requirement which was driven by functions which pass secrets into wasm. this will be added to wasmtime-wit-bindgen at a later date.
2. wiggle doesn't actually emit an event when calling a function which takes no arguments (see `&& func.params.len() > 0` in predicate), in this case we emit an event with the body `"call"`, to ensure these calls are observable.

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/crates/component-macro/src/bindgen.rs b/crates/component-macro/src/bindgen.rs
index 2a813ef5f..b589665b5 100644
--- a/crates/component-macro/src/bindgen.rs
+++ b/crates/component-macro/src/bindgen.rs
@@ -78,7 +78,16 @@ impl Parse for Config {
                     Opt::Async(val) => opts.async_ = val,
                     Opt::TrappableErrorType(val) => opts.trappable_error_type = val,
                     Opt::DuplicateIfNecessary(val) => opts.duplicate_if_necessary = val,
-                    Opt::OnlyInterfaces(val) => opts.only_interfaces = val,
+                    Opt::Interfaces(s) => {
+                        if source.is_some() {
+                            return Err(Error::new(s.span(), "cannot specify a second source"));
+                        }
+                        source = Some(Source::Inline(format!(
+                            "default world interfaces {{ {} }}",
+                            s.value()
+                        )));
+                        opts.only_interfaces = true;
+                    }
                     Opt::With(val) => opts.with.extend(val),
                 }
             }
@@ -136,7 +145,7 @@ mod kw {
     syn::custom_keyword!(trappable_error_type);
     syn::custom_keyword!(world);
     syn::custom_keyword!(duplicate_if_necessary);
-    syn::custom_keyword!(only_interfaces);
+    syn::custom_keyword!(interfaces);
     syn::custom_keyword!(with);
 }
 
@@ -148,7 +157,7 @@ enum Opt {
     Async(bool),
     TrappableErrorType(Vec<TrappableError>),
     DuplicateIfNecessary(bool),
-    OnlyInterfaces(bool),
+    Interfaces(syn::LitStr),
     With(HashMap<String, String>),
 }
 
@@ -198,10 +207,10 @@ impl Parse for Opt {
                     })
                     .collect(),
             ))
-        } else if l.peek(kw::only_interfaces) {
-            input.parse::<kw::only_interfaces>()?;
+        } else if l.peek(kw::interfaces) {
+            input.parse::<kw::interfaces>()?;
             input.parse::<Token![:]>()?;
-            Ok(Opt::OnlyInterfaces(input.parse::<syn::LitBool>()?.value))
+            Ok(Opt::Interfaces(input.parse::<syn::LitStr>()?))
         } else if l.peek(kw::with) {
             input.parse::<kw::with>()?;
             input.parse::<Token![:]>()?;
diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs
index 3dd7b740d..21550adef 100644
--- a/crates/wasmtime/src/component/mod.rs
+++ b/crates/wasmtime/src/component/mod.rs
@@ -292,11 +292,11 @@ pub(crate) use self::store::ComponentStoreData;
 ///         interface::ErrorType: RustErrorType,
 ///     },
 ///
-///     // Restrict the code generated to what's needed for the imported
-///     // interfaces of the world file provided. This option is most useful
-///     // in conjunction with the `with` option that permits remapping of
-///     // interface names in generated code.
-///     only_interfaces: true,
+///     // Restrict the code generated to what's needed for the interface
+///     // imports in the inlined WIT document fragment.
+///     interfaces: "
+///         import foo: package.foo;
+///     ",
 ///
 ///     // Remap interface names to module names, imported from elswhere.
 ///     // Using this option will prevent any code from being generated
diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs
index f564a5a95..5af48778c 100644
--- a/crates/wit-bindgen/src/lib.rs
+++ b/crates/wit-bindgen/src/lib.rs
@@ -1112,11 +1112,12 @@ impl<'a> InterfaceGenerator<'a> {
         }
 
         if self.gen.opts.tracing {
-            self.src.push_str(&format!(
+            uwrite!(
+                self.src,
                 "
                    let span = tracing::span!(
                        tracing::Level::TRACE,
-                       \"wit-bindgen guest import\",
+                       \"wit-bindgen import\",
                        module = \"{}\",
                        function = \"{}\",
                    );
@@ -1131,7 +1132,22 @@ impl<'a> InterfaceGenerator<'a> {
                     TypeOwner::None => "<no owner>",
                 },
                 func.name,
-            ));
+            );
+            let mut event_fields = func
+                .params
+                .iter()
+                .enumerate()
+                .map(|(i, (name, _ty))| {
+                    let name = to_rust_ident(&name);
+                    format!("{name} = tracing::field::debug(&arg{i})")
+                })
+                .collect::<Vec<String>>();
+            event_fields.push(format!("\"call\""));
+            uwrite!(
+                self.src,
+                "tracing::event!(tracing::Level::TRACE, {});\n",
+                event_fields.join(", ")
+            );
         }
 
         self.src.push_str("let host = get(caller.data_mut());\n");
@@ -1146,6 +1162,13 @@ impl<'a> InterfaceGenerator<'a> {
             uwrite!(self.src, ");\n");
         }
 
+        if self.gen.opts.tracing {
+            uwrite!(
+                self.src,
+                "tracing::event!(tracing::Level::TRACE, result = tracing::field::debug(&r), \"return\");"
+            );
+        }
+
         if self
             .special_case_trappable_error(owner, &func.results)
             .is_some()
@@ -1272,7 +1295,7 @@ impl<'a> InterfaceGenerator<'a> {
                 "
                    let span = tracing::span!(
                        tracing::Level::TRACE,
-                       \"wit-bindgen guest export\",
+                       \"wit-bindgen export\",
                        module = \"{}\",
                        function = \"{}\",
                    );


Test patch:
diff --git a/crates/component-macro/tests/codegen.rs b/crates/component-macro/tests/codegen.rs
index 3bcead048..ac3953402 100644
--- a/crates/component-macro/tests/codegen.rs
+++ b/crates/component-macro/tests/codegen.rs
@@ -21,13 +21,6 @@ macro_rules! gentest {
                     duplicate_if_necessary: true,
                 });
             }
-            mod interfaces_only {
-                wasmtime::component::bindgen!({
-                    path: $path,
-                    world: $name,
-                    only_interfaces: true,
-                });
-            }
         }
         // ...
     };
diff --git a/tests/all/component_model/bindgen/results.rs b/tests/all/component_model/bindgen/results.rs
index 19376a64a..a80341751 100644
--- a/tests/all/component_model/bindgen/results.rs
+++ b/tests/all/component_model/bindgen/results.rs
@@ -639,15 +639,10 @@ mod with_remapping {
 
     mod interfaces {
         wasmtime::component::bindgen!({
-            inline: "
-            default world result-playground {
-                import imports: interface {
-                    empty-error: func(a: float64) -> result<float64>
-                }
-
-                export empty-error: func(a: float64) -> result<float64>
+            interfaces: "
+            import imports: interface {
+                empty-error: func(a: float64) -> result<float64>
             }",
-            only_interfaces: true,
         });
     }