Repository: bytecodealliance/wasmtime
Pull request #1819 (2020-06-04 16:16:51)

Upgrade gimli to 0.21
* Updates version
* Hack for non-serializable Expression
* Remove .debug_frame writing


Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 7303ff56d..36257ade8 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -55,7 +55,7 @@ jobs:
         submodules: true
     - uses: ./.github/actions/install-rust
       with:
-        toolchain: nightly
+        toolchain: nightly-2020-06-03
     - run: cargo doc --no-deps --all --exclude wasmtime-cli --exclude test-programs --exclude cranelift-codegen-meta
     - run: cargo doc --package cranelift-codegen-meta --document-private-items
     - uses: actions/upload-artifact@v1
@@ -75,7 +75,7 @@ jobs:
         submodules: true
     - uses: ./.github/actions/install-rust
       with:
-        toolchain: nightly
+        toolchain: nightly-2020-06-03
 
     # Check some feature combinations of the `wasmtime` crate
     - run: cargo check --manifest-path crates/wasmtime/Cargo.toml --no-default-features
@@ -167,7 +167,7 @@ jobs:
             rust: beta
           - build: nightly
             os: ubuntu-latest
-            rust: nightly
+            rust: nightly-2020-06-03
           - build: macos
             os: macos-latest
             rust: stable
@@ -233,9 +233,9 @@ jobs:
     # Build and test lightbeam if we're using the nightly toolchain. Note that
     # Lightbeam tests fail right now, but we don't want to block on that.
     - run: cargo build --package lightbeam
-      if: matrix.rust == 'nightly'
+      if: matrix.rust == 'nightly-2020-06-03'
     - run: cargo test --package lightbeam
-      if: matrix.rust == 'nightly'
+      if: matrix.rust == 'nightly-2020-06-03'
       continue-on-error: true
       env:
         RUST_BACKTRACE: 1
diff --git a/Cargo.lock b/Cargo.lock
index 10a6a3bcd..8d73830f4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -252,6 +252,17 @@ version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 
+[[package]]
+name = "chrono"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
+dependencies = [
+ "num-integer",
+ "num-traits",
+ "time",
+]
+
 [[package]]
 name = "clap"
 version = "2.33.1"
@@ -912,15 +923,12 @@ dependencies = [
 
 [[package]]
 name = "gimli"
-version = "0.20.0"
+version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81dd6190aad0f05ddbbf3245c54ed14ca4aa6dd32f22312b70d8f168c3e3e633"
+checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c"
 dependencies = [
- "arrayvec",
- "byteorder",
  "fallible-iterator",
  "indexmap",
- "smallvec",
  "stable_deref_trait",
 ]
 
@@ -1132,6 +1140,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "matchers"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1"
+dependencies = [
+ "regex-automata",
+]
+
 [[package]]
 name = "maybe-uninit"
 version = "2.0.0"
@@ -1169,6 +1186,16 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238"
 
+[[package]]
+name = "num-integer"
+version = "0.1.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
+dependencies = [
+ "autocfg 1.0.0",
+ "num-traits",
+]
+
 [[package]]
 name = "num-traits"
 version = "0.2.11"
@@ -1657,6 +1684,16 @@ dependencies = [
  "thread_local",
 ]
 
+[[package]]
+name = "regex-automata"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4"
+dependencies = [
+ "byteorder",
+ "regex-syntax",
+]
+
 [[package]]
 name = "regex-syntax"
 version = "0.6.18"
@@ -1835,6 +1872,15 @@ dependencies = [
  "opaque-debug",
 ]
 
+[[package]]
+name = "sharded-slab"
+version = "0.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06d5a3f5166fb5b42a5439f2eee8b9de149e235961e3eb21c5808fc3ea17ff3e"
+dependencies = [
+ "lazy_static",
+]
+
 [[package]]
 name = "smallvec"
 version = "1.4.0"
@@ -2026,6 +2072,16 @@ dependencies = [
  "lazy_static",
 ]
 
+[[package]]
+name = "time"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
 [[package]]
 name = "toml"
 version = "0.5.6"
@@ -2035,6 +2091,79 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "tracing"
+version = "0.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a41f40ed0e162c911ac6fcb53ecdc8134c46905fdbbae8c50add462a538b495f"
+dependencies = [
+ "cfg-if",
+ "log",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99bbad0de3fd923c9c3232ead88510b783e5a4d16a6154adffa3d53308de984c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0aa83a9a47081cd522c09c81b31aec2c9273424976f922ad61c053b58350b715"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e0f8c7178e13481ff6765bd169b33e8d554c5d2bbede5e32c356194be02b9b9"
+dependencies = [
+ "lazy_static",
+ "log",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-serde"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6ccba2f8f16e0ed268fc765d9b7ff22e965e7185d32f8f1ec8294fe17d86e79"
+dependencies = [
+ "serde",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d53c40489aa69c9aed21ff483f26886ca8403df33bdc2d2f87c60c1617826d2"
+dependencies = [
+ "ansi_term",
+ "chrono",
+ "lazy_static",
+ "matchers",
+ "regex",
+ "serde",
+ "serde_json",
+ "sharded-slab",
+ "smallvec",
+ "tracing-core",
+ "tracing-log",
+ "tracing-serde",
+]
+
 [[package]]
 name = "traitobject"
 version = "0.1.0"
@@ -2480,6 +2609,7 @@ version = "0.17.0"
 dependencies = [
  "proptest",
  "thiserror",
+ "tracing",
  "wiggle-macro",
  "wiggle-test",
  "witx",
@@ -2512,7 +2642,11 @@ dependencies = [
 name = "wiggle-test"
 version = "0.17.0"
 dependencies = [
+ "env_logger",
  "proptest",
+ "thiserror",
+ "tracing",
+ "tracing-subscriber",
  "wiggle",
 ]
 
diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml
index e40baa0e2..0e2f5a20f 100644
--- a/cranelift/codegen/Cargo.toml
+++ b/cranelift/codegen/Cargo.toml
@@ -20,7 +20,7 @@ hashbrown = { version = "0.7", optional = true }
 target-lexicon = "0.10"
 log = { version = "0.4.6", default-features = false }
 serde = { version = "1.0.94", features = ["derive"], optional = true }
-gimli = { version = "0.20.0", default-features = false, features = ["write"], optional = true }
+gimli = { version = "0.21.0", default-features = false, features = ["write"], optional = true }
 smallvec = { version = "1.0.0" }
 thiserror = "1.0.4"
 byteorder = { version = "1.3.2", default-features = false }
diff --git a/cranelift/codegen/src/isa/unwind/systemv.rs b/cranelift/codegen/src/isa/unwind/systemv.rs
index 5985082f7..190226e8c 100644
--- a/cranelift/codegen/src/isa/unwind/systemv.rs
+++ b/cranelift/codegen/src/isa/unwind/systemv.rs
@@ -8,7 +8,6 @@ use thiserror::Error;
 use serde::{Deserialize, Serialize};
 
 type Register = u16;
-type Expression = Vec<u8>;
 
 /// Enumerate the errors possible in mapping Cranelift registers to their DWARF equivalent.
 #[allow(missing_docs)]
@@ -23,6 +22,8 @@ pub enum RegisterMappingError {
 }
 
 // This mirrors gimli's CallFrameInstruction, but is serializable
+// This excludes CfaExpression, Expression, ValExpression due to
+// https://github.com/gimli-rs/gimli/issues/513.
 // TODO: if gimli ever adds serialization support, remove this type
 #[derive(Clone, Debug, PartialEq, Eq)]
 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
@@ -30,15 +31,12 @@ pub(crate) enum CallFrameInstruction {
     Cfa(Register, i32),
     CfaRegister(Register),
     CfaOffset(i32),
-    CfaExpression(Expression),
     Restore(Register),
     Undefined(Register),
     SameValue(Register),
     Offset(Register, i32),
     ValOffset(Register, i32),
     Register(Register, Register),
-    Expression(Register, Expression),
-    ValExpression(Register, Expression),
     RememberState,
     RestoreState,
     ArgsSize(u32),
@@ -52,34 +50,33 @@ impl From<gimli::write::CallFrameInstruction> for CallFrameInstruction {
             CallFrameInstruction::Cfa(reg, offset) => Self::Cfa(reg.0, offset),
             CallFrameInstruction::CfaRegister(reg) => Self::CfaRegister(reg.0),
             CallFrameInstruction::CfaOffset(offset) => Self::CfaOffset(offset),
-            CallFrameInstruction::CfaExpression(expr) => Self::CfaExpression(expr.0),
             CallFrameInstruction::Restore(reg) => Self::Restore(reg.0),
             CallFrameInstruction::Undefined(reg) => Self::Undefined(reg.0),
             CallFrameInstruction::SameValue(reg) => Self::SameValue(reg.0),
             CallFrameInstruction::Offset(reg, offset) => Self::Offset(reg.0, offset),
             CallFrameInstruction::ValOffset(reg, offset) => Self::ValOffset(reg.0, offset),
             CallFrameInstruction::Register(reg1, reg2) => Self::Register(reg1.0, reg2.0),
-            CallFrameInstruction::Expression(reg, expr) => Self::Expression(reg.0, expr.0),
-            CallFrameInstruction::ValExpression(reg, expr) => Self::ValExpression(reg.0, expr.0),
             CallFrameInstruction::RememberState => Self::RememberState,
             CallFrameInstruction::RestoreState => Self::RestoreState,
             CallFrameInstruction::ArgsSize(size) => Self::ArgsSize(size),
+            _ => {
+                // Cranelift's unwind support does not generate `CallFrameInstruction`s with
+                // Expression at this moment, and it is not trivial to
+                // serialize such instructions.
+                panic!("CallFrameInstruction with Expression not supported");
+            }
         }
     }
 }
 
 impl Into<gimli::write::CallFrameInstruction> for CallFrameInstruction {
     fn into(self) -> gimli::write::CallFrameInstruction {
-        use gimli::{
-            write::{CallFrameInstruction, Expression},
-            Register,
-        };
+        use gimli::{write::CallFrameInstruction, Register};
 
         match self {
             Self::Cfa(reg, offset) => CallFrameInstruction::Cfa(Register(reg), offset),
             Self::CfaRegister(reg) => CallFrameInstruction::CfaRegister(Register(reg)),
             Self::CfaOffset(offset) => CallFrameInstruction::CfaOffset(offset),
-            Self::CfaExpression(expr) => CallFrameInstruction::CfaExpression(Expression(expr)),
             Self::Restore(reg) => CallFrameInstruction::Restore(Register(reg)),
             Self::Undefined(reg) => CallFrameInstruction::Undefined(Register(reg)),
             Self::SameValue(reg) => CallFrameInstruction::SameValue(Register(reg)),
@@ -88,12 +85,6 @@ impl Into<gimli::write::CallFrameInstruction> for CallFrameInstruction {
             Self::Register(reg1, reg2) => {
                 CallFrameInstruction::Register(Register(reg1), Register(reg2))
             }
-            Self::Expression(reg, expr) => {
-                CallFrameInstruction::Expression(Register(reg), Expression(expr))
-            }
-            Self::ValExpression(reg, expr) => {
-                CallFrameInstruction::ValExpression(Register(reg), Expression(expr))
-            }
             Self::RememberState => CallFrameInstruction::RememberState,
             Self::RestoreState => CallFrameInstruction::RestoreState,
             Self::ArgsSize(size) => CallFrameInstruction::ArgsSize(size),
diff --git a/crates/debug/Cargo.toml b/crates/debug/Cargo.toml
index 8cbae2cc8..acd417add 100644
--- a/crates/debug/Cargo.toml
+++ b/crates/debug/Cargo.toml
@@ -12,7 +12,7 @@ readme = "README.md"
 edition = "2018"
 
 [dependencies]
-gimli = "0.20.0"
+gimli = "0.21.0"
 wasmparser = "0.57.0"
 faerie = "0.15.0"
 wasmtime-environ = { path = "../environ", version = "0.17.0" }
diff --git a/crates/debug/src/transform/attr.rs b/crates/debug/src/transform/attr.rs
index 1e74ca9a5..cb654fe79 100644
--- a/crates/debug/src/transform/attr.rs
+++ b/crates/debug/src/transform/attr.rs
@@ -228,7 +228,7 @@ where
                                 let mut found_expr: Option<write::Expression> = None;
                                 for (_, _, expr) in &exprs {
                                     if let Some(ref prev_expr) = found_expr {
-                                        if expr.0.eq(&prev_expr.0) {
+                                        if expr == prev_expr {
                                             continue; // the same expression
                                         }
                                         found_expr = None;
diff --git a/crates/debug/src/transform/expression.rs b/crates/debug/src/transform/expression.rs
index c7b71c003..8bd3a7630 100644
--- a/crates/debug/src/transform/expression.rs
+++ b/crates/debug/src/transform/expression.rs
@@ -215,7 +215,7 @@ impl CompiledExpression {
 
     pub fn build(&self) -> Option<write::Expression> {
         if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() {
-            return Some(write::Expression(code.to_vec()));
+            return Some(write::Expression::raw(code.to_vec()));
         }
         // locals found, not supported
         None
@@ -245,7 +245,7 @@ impl CompiledExpression {
                     BuildWithLocalsResult::Empty => None,
                     BuildWithLocalsResult::Simple(it, code) => it
                         .next()
-                        .map(|(addr, len)| Ok((addr, len, write::Expression(code.to_vec())))),
+                        .map(|(addr, len)| Ok((addr, len, write::Expression::raw(code.to_vec())))),
                     BuildWithLocalsResult::Ranges(it) => it.next().map(|r| {
                         r.map(|(func_index, start, end, code_buf)| {
                             (
@@ -254,7 +254,7 @@ impl CompiledExpression {
                                     addend: start as i64,
                                 },
                                 (end - start) as u64,
-                                write::Expression(code_buf),
+                                write::Expression::raw(code_buf),
                             )
                         })
                     }),
@@ -415,7 +415,7 @@ where
             });
         } else {
             let pos = pc.offset_from(&expr.0).into_u64() as usize;
-            let op = Operation::parse(&mut pc, &expr.0, encoding)?;
+            let op = Operation::parse(&mut pc, encoding)?;
             match op {
                 Operation::FrameOffset { offset } => {
                     // Expand DW_OP_fpreg into frame location and DW_OP_plus_uconst.
@@ -435,7 +435,8 @@ where
                     code_chunk.extend(writer.into_vec());
                     continue;
                 }
-                Operation::Literal { .. }
+                Operation::UnsignedConstant { .. }
+                | Operation::SignedConstant { .. }
                 | Operation::PlusConstant { .. }
                 | Operation::Piece { .. } => (),
                 Operation::StackValue => {
diff --git a/crates/debug/src/transform/refs.rs b/crates/debug/src/transform/refs.rs
index 1780f0649..4fa0ce99d 100644
--- a/crates/debug/src/transform/refs.rs
+++ b/crates/debug/src/transform/refs.rs
@@ -58,7 +58,7 @@ impl UnitRefsMap {
         for (die_id, attr_name, offset) in refs.refs {
             let die = comp_unit.get_mut(die_id);
             if let Some(unit_id) = self.map.get(&offset) {
-                die.set(attr_name, write::AttributeValue::ThisUnitEntryRef(*unit_id));
+                die.set(attr_name, write::AttributeValue::UnitRef(*unit_id));
             }
         }
     }
@@ -102,7 +102,9 @@ impl DebugInfoRefsMap {
                 if let Some((id, entry_id)) = self.map.get(&offset) {
                     die.set(
                         attr_name,
-                        write::AttributeValue::AnyUnitEntryRef((*id, *entry_id)),
+                        write::AttributeValue::DebugInfoRef(write::Reference::Entry(
+                            *id, *entry_id,
+                        )),
                     );
                 }
             }
diff --git a/crates/debug/src/transform/simulate.rs b/crates/debug/src/transform/simulate.rs
index cb8b38c9e..4f06ba8aa 100644
--- a/crates/debug/src/transform/simulate.rs
+++ b/crates/debug/src/transform/simulate.rs
@@ -262,7 +262,7 @@ fn generate_vars(
             var.set(gimli::DW_AT_name, write::AttributeValue::StringRef(name_id));
             var.set(
                 gimli::DW_AT_type,
-                write::AttributeValue::ThisUnitEntryRef(type_die_id),
+                write::AttributeValue::UnitRef(type_die_id),
             );
             var.set(
                 gimli::DW_AT_location,
diff --git a/crates/debug/src/transform/unit.rs b/crates/debug/src/transform/unit.rs
index a04f11b4e..01835ccdf 100644
--- a/crates/debug/src/transform/unit.rs
+++ b/crates/debug/src/transform/unit.rs
@@ -132,7 +132,7 @@ where
     // Build DW_TAG_pointer_type for `WebAssemblyPtrWrapper<T>*`:
     //  .. DW_AT_type = <wrapper_die>
     add_tag!(parent_id, gimli::DW_TAG_pointer_type => wrapper_ptr_type as wrapper_ptr_type_id {
-        gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(wrapper_die_id)
+        gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_die_id)
     });
 
     let base_type_id = pointer_type_entry.attr_value(gimli::DW_AT_type)?;
@@ -166,7 +166,7 @@ where
     //  .. DW_AT_location = 0
     add_tag!(wrapper_die_id, gimli::DW_TAG_member => m_die as m_die_id {
         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("__ptr")),
-        gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(wp_die_id),
+        gimli::DW_AT_type = write::AttributeValue::UnitRef(wp_die_id),
         gimli::DW_AT_data_member_location = write::AttributeValue::Data1(0)
     });
 
@@ -180,10 +180,10 @@ where
     add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
         gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add("resolve_vmctx_memory_ptr")),
         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("ptr")),
-        gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(ptr_type_id)
+        gimli::DW_AT_type = write::AttributeValue::UnitRef(ptr_type_id)
     });
     add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
-        gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(wrapper_ptr_type_id),
+        gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
         gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
     });
 
@@ -197,10 +197,10 @@ where
     add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
         gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add("resolve_vmctx_memory_ptr")),
         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("operator*")),
-        gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(ref_type_id)
+        gimli::DW_AT_type = write::AttributeValue::UnitRef(ref_type_id)
     });
     add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
-        gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(wrapper_ptr_type_id),
+        gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
         gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
     });
 
@@ -214,10 +214,10 @@ where
     add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
         gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add("resolve_vmctx_memory_ptr")),
         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("operator->")),
-        gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(ptr_type_id)
+        gimli::DW_AT_type = write::AttributeValue::UnitRef(ptr_type_id)
     });
     add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
-        gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(wrapper_ptr_type_id),
+        gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
         gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
     });
 
diff --git a/crates/debug/src/transform/utils.rs b/crates/debug/src/transform/utils.rs
index f6a30ab21..d8ad91429 100644
--- a/crates/debug/src/transform/utils.rs
+++ b/crates/debug/src/transform/utils.rs
@@ -56,7 +56,7 @@ pub(crate) fn add_internal_types(
     //  .. DW_AT_type = <memory_byte_die>
     add_tag!(root_id, gimli::DW_TAG_pointer_type => memory_bytes_die as memory_bytes_die_id {
         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("u8*")),
-        gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(memory_byte_die_id)
+        gimli::DW_AT_type = write::AttributeValue::UnitRef(memory_byte_die_id)
     });
 
     // Create artificial VMContext type and its reference for convinience viewing
@@ -87,7 +87,7 @@ pub(crate) fn add_internal_types(
             //  .. DW_AT_data_member_location = `memory_offset`
             add_tag!(vmctx_die_id, gimli::DW_TAG_member => m_die as m_die_id {
                 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("memory")),
-                gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(memory_bytes_die_id),
+                gimli::DW_AT_type = write::AttributeValue::UnitRef(memory_bytes_die_id),
                 gimli::DW_AT_data_member_location = write::AttributeValue::Udata(memory_offset as u64)
             });
         }
@@ -102,7 +102,7 @@ pub(crate) fn add_internal_types(
     //  .. DW_AT_type = <vmctx_die>
     add_tag!(root_id, gimli::DW_TAG_pointer_type => vmctx_ptr_die as vmctx_ptr_die_id {
         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext*")),
-        gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(vmctx_die_id)
+        gimli::DW_AT_type = write::AttributeValue::UnitRef(vmctx_die_id)
     });
 
     // Build vmctx_die's DW_TAG_subprogram for `set` method:
@@ -116,7 +116,7 @@ pub(crate) fn add_internal_types(
         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("set"))
     });
     add_tag!(vmctx_set_id, gimli::DW_TAG_formal_parameter => vmctx_set_this_param as vmctx_set_this_param_id {
-        gimli::DW_AT_type = write::AttributeValue::ThisUnitEntryRef(vmctx_ptr_die_id),
+        gimli::DW_AT_type = write::AttributeValue::UnitRef(vmctx_ptr_die_id),
         gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
     });
 
@@ -157,7 +157,7 @@ pub(crate) fn append_vmctx_info(
     );
     var_die.set(
         gimli::DW_AT_type,
-        write::AttributeValue::ThisUnitEntryRef(vmctx_die_id),
+        write::AttributeValue::UnitRef(vmctx_die_id),
     );
     var_die.set(gimli::DW_AT_location, loc);
 
diff --git a/crates/debug/src/write_debuginfo.rs b/crates/debug/src/write_debuginfo.rs
index accdb17d4..8fe80f0ce 100644
--- a/crates/debug/src/write_debuginfo.rs
+++ b/crates/debug/src/write_debuginfo.rs
@@ -1,6 +1,6 @@
 use faerie::artifact::{Decl, SectionKind};
 use faerie::*;
-use gimli::write::{Address, DebugFrame, Dwarf, EndianVec, FrameTable, Result, Sections, Writer};
+use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer};
 use gimli::{RunTimeEndian, SectionId};
 
 #[derive(Clone)]
@@ -30,6 +30,9 @@ pub fn emit_dwarf(
 
     let mut sections = Sections::new(WriterRelocate::new(endian, symbol_resolver));
     dwarf.write(&mut sections)?;
+    if let Some(frames) = frames {
+        frames.write_debug_frame(&mut sections.debug_frame)?;
+    }
     sections.for_each_mut(|id, s| -> anyhow::Result<()> {
         artifact.declare_with(
             id.name(),
@@ -55,29 +58,6 @@ pub fn emit_dwarf(
         Ok(())
     })?;
 
-    if let Some(frames) = frames {
-        let mut debug_frame = DebugFrame::from(WriterRelocate::new(endian, symbol_resolver));
-        frames.write_debug_frame(&mut debug_frame).unwrap();
-        artifact.declare_with(
-            SectionId::DebugFrame.name(),
-            Decl::section(SectionKind::Debug),
-            debug_frame.writer.take(),
-        )?;
-        for reloc in &debug_frame.relocs {
-            artifact.link_with(
-                faerie::Link {
-                    from: SectionId::DebugFrame.name(),
-                    to: &reloc.name,
-                    at: u64::from(reloc.offset),
-                },
-                faerie::Reloc::Debug {
-                    size: reloc.size,
-                    addend: reloc.addend as i32,
-                },
-            )?;
-        }
-    }
-
     Ok(())
 }
 
diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml
index 822522da2..c7c38facd 100644
--- a/crates/jit/Cargo.toml
+++ b/crates/jit/Cargo.toml
@@ -29,7 +29,7 @@ more-asserts = "0.2.1"
 anyhow = "1.0"
 cfg-if = "0.1.9"
 log = "0.4"
-gimli = { version = "0.20.0", default-features = false, features = ["write"] }
+gimli = { version = "0.21.0", default-features = false, features = ["write"] }
 
 [target.'cfg(target_os = "windows")'.dependencies]
 winapi = { version = "0.3.8", features = ["winnt", "impl-default"] }
diff --git a/crates/profiling/Cargo.toml b/crates/profiling/Cargo.toml
index 79b9d3f32..c18935ecd 100644
--- a/crates/profiling/Cargo.toml
+++ b/crates/profiling/Cargo.toml
@@ -13,7 +13,7 @@ edition = "2018"
 [dependencies]
 anyhow = "1.0"
 cfg-if = "0.1"
-gimli = { version = "0.20.0", optional = true }
+gimli = { version = "0.21.0", optional = true }
 lazy_static = "1.4"
 libc = { version = "0.2.60", default-features = false }
 scroll = { version = "0.10.1", optional = true }
diff --git a/crates/wasi-common/Cargo.toml b/crates/wasi-common/Cargo.toml
index c24127a33..9a8f46db3 100644
--- a/crates/wasi-common/Cargo.toml
+++ b/crates/wasi-common/Cargo.toml
@@ -41,10 +41,10 @@ maintenance = { status = "actively-developed" }
 
 [features]
 default = ["trace_log"]
-# This feature enables `log::trace` calls in syscalls shims, in effect
-# emulating something like `strace`. This feature is an opt-out and hence
-# enabled by default.
-trace_log = []
+# This feature enables the `tracing` logs in the calls to target the `log`
+# ecosystem of backends (e.g. `env_logger`. Disable this if you want to use
+# `tracing-subscriber`.
+trace_log = [ "wiggle/tracing_log" ]
 # Need to make the wiggle_metadata feature available to consumers of this
 # crate if they want the snapshots to have metadata available.
 wiggle_metadata = ["wiggle/wiggle_metadata"]
diff --git a/crates/wiggle/Cargo.toml b/crates/wiggle/Cargo.toml
index c6f6adb42..55a4ceabb 100644
--- a/crates/wiggle/Cargo.toml
+++ b/crates/wiggle/Cargo.toml
@@ -14,6 +14,7 @@ include = ["src/**/*", "LICENSE"]
 thiserror = "1"
 witx = { path = "../wasi-common/WASI/tools/witx", version = "0.8.5", optional = true }
 wiggle-macro = { path = "macro", version = "0.17.0" }
+tracing = "0.1.15"
 
 [badges]
 maintenance = { status = "actively-developed" }
@@ -23,13 +24,16 @@ wiggle-test = { path = "test-helpers" }
 proptest = "0.9"
 
 [features]
-# These features have no effect on the users of this crate. They are only
-# necessary for testing.
 # The wiggle proc-macro emits some code (inside `pub mod metadata`) guarded
 # by the `wiggle_metadata` feature flag. We use this feature flag so that
 # users of wiggle are not forced to take a direct dependency on the `witx`
 # crate unless they want it.
 wiggle_metadata = ['witx', "wiggle-macro/wiggle_metadata"]
-# In order to test that the contents of this metadata module meet
-# expectations, we must have this feature enabled for the crate by default.
-default = ["wiggle_metadata"]
+
+# The `tracing` crate can use the `log` ecosystem of backends with this
+# non-default feature. We don't need to provide this by default, but its
+# useful for users that don't want to use `tracing-subscriber` to get
+# the logs out of wiggle-generated libraries.
+tracing_log = [ "tracing/log" ]
+
+default = ["wiggle_metadata" ]
diff --git a/crates/wiggle/generate/src/config.rs b/crates/wiggle/generate/src/config.rs
index 6eecf28c9..22ff88aa2 100644
--- a/crates/wiggle/generate/src/config.rs
+++ b/crates/wiggle/generate/src/config.rs
@@ -27,23 +27,35 @@ pub enum ConfigField {
     Error(ErrorConf),
 }
 
-impl ConfigField {
-    pub fn parse_pair(ident: &str, value: ParseStream, err_loc: Span) -> Result<Self> {
-        match ident {
-            "witx" => Ok(ConfigField::Witx(WitxConf::Paths(value.parse()?))),
-            "witx_literal" => Ok(ConfigField::Witx(WitxConf::Literal(value.parse()?))),
-            "ctx" => Ok(ConfigField::Ctx(value.parse()?)),
-            "errors" => Ok(ConfigField::Error(value.parse()?)),
-            _ => Err(Error::new(err_loc, "expected `witx`, `ctx`, or `errors`")),
-        }
-    }
+mod kw {
+    syn::custom_keyword!(witx);
+    syn::custom_keyword!(witx_literal);
+    syn::custom_keyword!(ctx);
+    syn::custom_keyword!(errors);
 }
 
 impl Parse for ConfigField {
     fn parse(input: ParseStream) -> Result<Self> {
-        let id: Ident = input.parse()?;
-        let _colon: Token![:] = input.parse()?;
-        Self::parse_pair(id.to_string().as_ref(), input, id.span())
+        let lookahead = input.lookahead1();
+        if lookahead.peek(kw::witx) {
+            input.parse::<kw::witx>()?;
+            input.parse::<Token![:]>()?;
+            Ok(ConfigField::Witx(WitxConf::Paths(input.parse()?)))
+        } else if lookahead.peek(kw::witx_literal) {
+            input.parse::<kw::witx_literal>()?;
+            input.parse::<Token![:]>()?;
+            Ok(ConfigField::Witx(WitxConf::Literal(input.parse()?)))
+        } else if lookahead.peek(kw::ctx) {
+            input.parse::<kw::ctx>()?;
+            input.parse::<Token![:]>()?;
+            Ok(ConfigField::Ctx(input.parse()?))
+        } else if lookahead.peek(kw::errors) {
+            input.parse::<kw::errors>()?;
+            input.parse::<Token![:]>()?;
+            Ok(ConfigField::Error(input.parse()?))
+        } else {
+            Err(lookahead.error())
+        }
     }
 }
 
diff --git a/crates/wiggle/generate/src/error_transform.rs b/crates/wiggle/generate/src/error_transform.rs
index 80cb56f92..3598e221d 100644
--- a/crates/wiggle/generate/src/error_transform.rs
+++ b/crates/wiggle/generate/src/error_transform.rs
@@ -11,6 +11,9 @@ pub struct ErrorTransform {
 }
 
 impl ErrorTransform {
+    pub fn empty() -> Self {
+        Self { m: Vec::new() }
+    }
     pub fn new(conf: &ErrorConf, doc: &Document) -> Result<Self, Error> {
         let mut richtype_identifiers = HashMap::new();
         let m = conf.iter().map(|(ident, field)|
diff --git a/crates/wiggle/generate/src/funcs.rs b/crates/wiggle/generate/src/funcs.rs
index 2c8cf2b35..0baa8cd39 100644
--- a/crates/wiggle/generate/src/funcs.rs
+++ b/crates/wiggle/generate/src/funcs.rs
@@ -5,11 +5,12 @@ use crate::error_transform::ErrorTransform;
 use crate::lifetimes::anon_lifetime;
 use crate::module_trait::passed_by_reference;
 use crate::names::Names;
+use crate::types::WiggleType;
 
 pub fn define_func(
     names: &Names,
+    module: &witx::Module,
     func: &witx::InterfaceFunc,
-    trait_name: TokenStream,
     errxform: &ErrorTransform,
 ) -> TokenStream {
     let funcname = func.name.as_str();
@@ -49,19 +50,20 @@ pub fn define_func(
     let ret_err = coretype
         .ret
         .map(|ret| {
-            let name = ret.param.name.as_str();
+            let name = names.func_param(&ret.param.name);
             let conversion = if let Some(user_err) = errxform.for_abi_error(&ret.param.tref) {
                 let method = names.user_error_conversion_method(&user_err);
-                quote!(#abi_ret::from(UserErrorConversion::#method(ctx, e)))
+                quote!(UserErrorConversion::#method(ctx, e))
             } else {
-                quote!(#abi_ret::from(e))
+                quote!(e)
             };
             quote! {
-                #[cfg(feature = "trace_log")]
-                {
-                    log::trace!("     | {}={:?}", #name, e);
-                }
-                return #conversion;
+                let e = #conversion;
+                #rt::tracing::event!(
+                    #rt::tracing::Level::TRACE,
+                    #name = #rt::tracing::field::debug(&e),
+                );
+                return #abi_ret::from(e);
             }
         })
         .unwrap_or_else(|| quote!(()));
@@ -76,7 +78,7 @@ pub fn define_func(
             let err_method = names.guest_error_conversion_method(&tref);
             quote! {
                 let e = #rt::GuestError::InFunc { funcname: #funcname, location: #location, err: Box::new(e.into()) };
-                let err: #err_typename = GuestErrorConversion::#err_method(ctx, e); // XXX replace with conversion method on trait!
+                let err: #err_typename = GuestErrorConversion::#err_method(ctx, e);
                 return #abi_ret::from(err);
             }
         } else {
@@ -99,6 +101,23 @@ pub fn define_func(
         }
     });
 
+    let log_marshalled_args = if func.params.len() > 0 {
+        let rt = names.runtime_mod();
+        let args = func.params.iter().map(|param| {
+            let name = names.func_param(&param.name);
+            if param.impls_display() {
+                quote!( #name = #rt::tracing::field::display(&#name) )
+            } else {
+                quote!( #name = #rt::tracing::field::debug(&#name) )
+            }
+        });
+        quote! {
+            #rt::tracing::event!(#rt::tracing::Level::TRACE, #(#args),*);
+        }
+    } else {
+        quote!()
+    };
+
     let (trait_rets, trait_bindings) = if func.results.len() < 2 {
         (quote!({}), quote!(_))
     } else {
@@ -109,29 +128,16 @@ pub fn define_func(
             .map(|result| names.func_param(&result.name))
             .collect();
         let bindings = quote!((#(#trait_rets),*));
-        let names: Vec<_> = func
-            .results
-            .iter()
-            .skip(1)
-            .map(|result| {
-                let name = names.func_param(&result.name);
-                let fmt = match &*result.tref.type_() {
-                    witx::Type::Builtin(_)
-                    | witx::Type::Enum(_)
-                    | witx::Type::Flags(_)
-                    | witx::Type::Handle(_)
-                    | witx::Type::Int(_) => "{}",
-                    _ => "{:?}",
-                };
-                format!("{}={}", name.to_string(), fmt)
-            })
-            .collect();
-        let trace_fmt = format!("     | result=({})", names.join(","));
-        let rets = quote! {
-            #[cfg(feature = "trace_log")]
-            {
-                log::trace!(#trace_fmt, #(#trait_rets),*);
+        let trace_rets = func.results.iter().skip(1).map(|result| {
+            let name = names.func_param(&result.name);
+            if result.tref.impls_display() {
+                quote!(#name = #rt::tracing::field::display(&#name))
+            } else {
+                quote!(#name = #rt::tracing::field::debug(&#name))
             }
+        });
+        let rets = quote! {
+            #rt::tracing::event!(#rt::tracing::Level::TRACE, #(#trace_rets),*);
             (#(#trait_rets),*)
         };
         (rets, bindings)
@@ -151,37 +157,32 @@ pub fn define_func(
         let err_typename = names.type_ref(&err_type, anon_lifetime());
         quote! {
             let success:#err_typename = #rt::GuestErrorType::success();
-            #[cfg(feature = "trace_log")]
-            {
-                log::trace!("     | errno={}", success);
-            }
+            #rt::tracing::event!(
+                #rt::tracing::Level::TRACE,
+                success=#rt::tracing::field::display(&success)
+            );
             #abi_ret::from(success)
         }
     } else {
         quote!()
     };
 
-    let (placeholders, args): (Vec<_>, Vec<_>) = func
-        .params
-        .iter()
-        .map(|param| {
-            let name = names.func_param(&param.name);
-            let fmt = if passed_by_reference(&*param.tref.type_()) {
-                "{:?}"
-            } else {
-                "{}"
-            };
-            (format!("{}={}", name.to_string(), fmt), quote!(#name))
-        })
-        .unzip();
-    let trace_fmt = format!("{}({})", ident.to_string(), placeholders.join(","));
+    let trait_name = names.trait_name(&module.name);
+    let mod_name = &module.name.as_str();
+    let func_name = &func.name.as_str();
+
     quote!(pub fn #ident(#abi_args) -> #abi_ret {
+        let _span = #rt::tracing::span!(
+            #rt::tracing::Level::TRACE,
+            "wiggle abi",
+            module = #mod_name,
+            function = #func_name
+        );
+        let _enter = _span.enter();
+
         #(#marshal_args)*
         #(#marshal_rets_pre)*
-        #[cfg(feature = "trace_log")]
-        {
-            log::trace!(#trace_fmt, #(#args),*);
-        }
+        #log_marshalled_args
         let #trait_bindings  = match #trait_name::#ident(ctx, #(#trait_args),*) {
             Ok(#trait_bindings) => { #trait_rets },
             Err(e) => { #ret_err },
diff --git a/crates/wiggle/generate/src/lib.rs b/crates/wiggle/generate/src/lib.rs
index bd6d5d9e4..f1913163c 100644
--- a/crates/wiggle/generate/src/lib.rs
+++ b/crates/wiggle/generate/src/lib.rs
@@ -49,10 +49,9 @@ pub fn generate(doc: &witx::Document, names: &Names, errs: &ErrorTransform) -> T
     };
     let modules = doc.modules().map(|module| {
         let modname = names.module(&module.name);
-        let trait_name = names.trait_name(&module.name);
         let fs = module
             .funcs()
-            .map(|f| define_func(&names, &f, quote!(#trait_name), &errs));
+            .map(|f| define_func(&names, &module, &f, &errs));
         let modtrait = define_module_trait(&names, &module, &errs);
         let ctx_type = names.ctx_type();
         quote!(
diff --git a/crates/wiggle/generate/src/types/enum.rs b/crates/wiggle/generate/src/types/enum.rs
index a3a009604..cef56065e 100644
--- a/crates/wiggle/generate/src/types/enum.rs
+++ b/crates/wiggle/generate/src/types/enum.rs
@@ -112,3 +112,9 @@ pub(super) fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype
         }
     }
 }
+
+impl super::WiggleType for witx::EnumDatatype {
+    fn impls_display(&self) -> bool {
+        true
+    }
+}
diff --git a/crates/wiggle/generate/src/types/flags.rs b/crates/wiggle/generate/src/types/flags.rs
index 7759e7e6a..f87261abc 100644
--- a/crates/wiggle/generate/src/types/flags.rs
+++ b/crates/wiggle/generate/src/types/flags.rs
@@ -180,3 +180,9 @@ pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDataty
 
     }
 }
+
+impl super::WiggleType for witx::FlagsDatatype {
+    fn impls_display(&self) -> bool {
+        true
+    }
+}
diff --git a/crates/wiggle/generate/src/types/handle.rs b/crates/wiggle/generate/src/types/handle.rs
index c28b77d88..310c9e7d6 100644
--- a/crates/wiggle/generate/src/types/handle.rs
+++ b/crates/wiggle/generate/src/types/handle.rs
@@ -82,3 +82,9 @@ pub(super) fn define_handle(
 
     }
 }
+
+impl super::WiggleType for witx::HandleDatatype {
+    fn impls_display(&self) -> bool {
+        true
+    }
+}
diff --git a/crates/wiggle/generate/src/types/int.rs b/crates/wiggle/generate/src/types/int.rs
index 43e699768..d916b65b7 100644
--- a/crates/wiggle/generate/src/types/int.rs
+++ b/crates/wiggle/generate/src/types/int.rs
@@ -92,3 +92,9 @@ pub(super) fn define_int(names: &Names, name: &witx::Id, i: &witx::IntDatatype)
 
     }
 }
+
+impl super::WiggleType for witx::IntDatatype {
+    fn impls_display(&self) -> bool {
+        true
+    }
+}
diff --git a/crates/wiggle/generate/src/types/mod.rs b/crates/wiggle/generate/src/types/mod.rs
index 19b8aa948..6ed29af94 100644
--- a/crates/wiggle/generate/src/types/mod.rs
+++ b/crates/wiggle/generate/src/types/mod.rs
@@ -91,3 +91,56 @@ fn atom_token(atom: witx::AtomType) -> TokenStream {
         witx::AtomType::F64 => quote!(f64),
     }
 }
+
+pub trait WiggleType {
+    fn impls_display(&self) -> bool;
+}
+
+impl WiggleType for witx::TypeRef {
+    fn impls_display(&self) -> bool {
+        match self {
+            witx::TypeRef::Name(alias_to) => (&*alias_to).impls_display(),
+            witx::TypeRef::Value(v) => (&*v).impls_display(),
+        }
+    }
+}
+
+impl WiggleType for witx::NamedType {
+    fn impls_display(&self) -> bool {
+        self.tref.impls_display()
+    }
+}
+
+impl WiggleType for witx::Type {
+    fn impls_display(&self) -> bool {
+        match self {
+            witx::Type::Enum(x) => x.impls_display(),
+            witx::Type::Int(x) => x.impls_display(),
+            witx::Type::Flags(x) => x.impls_display(),
+            witx::Type::Struct(x) => x.impls_display(),
+            witx::Type::Union(x) => x.impls_display(),
+            witx::Type::Handle(x) => x.impls_display(),
+            witx::Type::Builtin(x) => x.impls_display(),
+            witx::Type::Pointer { .. }
+            | witx::Type::ConstPointer { .. }
+            | witx::Type::Array { .. } => false,
+        }
+    }
+}
+
+impl WiggleType for witx::BuiltinType {
+    fn impls_display(&self) -> bool {
+        true
+    }
+}
+
+impl WiggleType for witx::InterfaceFuncParam {
+    fn impls_display(&self) -> bool {
+        match &*self.tref.type_() {
+            witx::Type::Struct { .. }
+            | witx::Type::Union { .. }
+            | witx::Type::Builtin(witx::BuiltinType::String { .. }) => false,
+            _ => self.tref.impls_display(),
+        }
+    }
+}
diff --git a/crates/wiggle/generate/src/types/struct.rs b/crates/wiggle/generate/src/types/struct.rs
index 47cd32d1e..5b3a02590 100644
--- a/crates/wiggle/generate/src/types/struct.rs
+++ b/crates/wiggle/generate/src/types/struct.rs
@@ -133,3 +133,9 @@ pub(super) fn define_struct(
         #transparent
     }
 }
+
+impl super::WiggleType for witx::StructDatatype {
+    fn impls_display(&self) -> bool {
+        false
+    }
+}
diff --git a/crates/wiggle/generate/src/types/union.rs b/crates/wiggle/generate/src/types/union.rs
index 0c357756e..ecc3253f3 100644
--- a/crates/wiggle/generate/src/types/union.rs
+++ b/crates/wiggle/generate/src/types/union.rs
@@ -108,3 +108,9 @@ pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDataty
         }
     }
 }
+
+impl super::WiggleType for witx::UnionDatatype {
+    fn impls_display(&self) -> bool {
+        false
+    }
+}
diff --git a/crates/wiggle/src/lib.rs b/crates/wiggle/src/lib.rs
index 000080ad8..cbc92f266 100644
--- a/crates/wiggle/src/lib.rs
+++ b/crates/wiggle/src/lib.rs
@@ -16,6 +16,8 @@ mod error;
 mod guest_type;
 mod region;
 
+pub extern crate tracing;
+
 pub use borrow::BorrowChecker;
 use borrow::BorrowHandle;
 pub use error::GuestError;


Test patch:
diff --git a/cranelift/filetests/Cargo.toml b/cranelift/filetests/Cargo.toml
index 3fc5cb8df..974c8fbee 100644
--- a/cranelift/filetests/Cargo.toml
+++ b/cranelift/filetests/Cargo.toml
@@ -19,7 +19,7 @@ cranelift-preopt = { path = "../preopt", version = "0.64.0" }
 byteorder = { version = "1.3.2", default-features = false }
 file-per-thread-logger = "0.1.2"
 filecheck = "0.5.0"
-gimli = { version = "0.20.0", default-features = false, features = ["read"] }
+gimli = { version = "0.21.0", default-features = false, features = ["read"] }
 log = "0.4.6"
 memmap = "0.7.0"
 num_cpus = "1.8.0"
diff --git a/crates/wiggle/test-helpers/Cargo.toml b/crates/wiggle/test-helpers/Cargo.toml
index da548e395..0df67f862 100644
--- a/crates/wiggle/test-helpers/Cargo.toml
+++ b/crates/wiggle/test-helpers/Cargo.toml
@@ -12,7 +12,14 @@ include = ["src/**/*", "LICENSE"]
 
 [dependencies]
 proptest = "0.9"
-wiggle = { path = ".." }
+wiggle = { path = "..", features = ["tracing_log"] }
+
+[dev-dependencies]
+thiserror = "1.0"
+tracing = "0.1.14"
+tracing-subscriber = "0.2.4"
+env_logger = "0.7"
+
 
 [badges]
 maintenance = { status = "actively-developed" }
diff --git a/crates/wiggle/test-helpers/examples/tracing.rs b/crates/wiggle/test-helpers/examples/tracing.rs
new file mode 100644
index 000000000..5b7eeb818
--- /dev/null
+++ b/crates/wiggle/test-helpers/examples/tracing.rs
@@ -0,0 +1,123 @@
+use wiggle_test::{impl_errno, HostMemory, WasiCtx};
+
+/// The `errors` argument to the wiggle gives us a hook to map a rich error
+/// type like this one (typical of wiggle use cases in wasi-common and beyond)
+/// down to the flat error enums that witx can specify.
+#[derive(Debug, thiserror::Error)]
+pub enum RichError {
+    #[error("Invalid argument: {0}")]
+    InvalidArg(String),
+    #[error("Won't cross picket line: {0}")]
+    PicketLine(String),
+}
+
+// Define an errno with variants corresponding to RichError. Use it in a
+// trivial function.
+wiggle::from_witx!({
+witx_literal: "
+(typename $errno (enum u8 $ok $invalid_arg $picket_line))
+(typename $s (struct (field $f1 (@witx usize)) (field $f2 (@witx pointer u8))))
+(typename $t (struct (field $f1 u32) (field $f2 f32)))
+(module $one_error_conversion
+  (@interface func (export \"foo\")
+     (param $strike u32)
+     (param $s $s)
+     (result $err $errno)
+     (result $t $t)))
+    ",
+    ctx: WasiCtx,
+    errors: { errno => RichError },
+});
+
+// The impl of GuestErrorConversion works just like in every other test where
+// we have a single error type with witx `$errno` with the success called `$ok`
+impl_errno!(types::Errno, types::GuestErrorConversion);
+
+/// When the `errors` mapping in witx is non-empty, we need to impl the
+/// types::UserErrorConversion trait that wiggle generates from that mapping.
+impl<'a> types::UserErrorConversion for WasiCtx<'a> {
+    fn errno_from_rich_error(&self, e: RichError) -> types::Errno {
+        wiggle::tracing::debug!(
+            rich_error = wiggle::tracing::field::debug(&e),
+            "error conversion"
+        );
+        // WasiCtx can collect a Vec<String> log so we can test this. We're
+        // logging the Display impl that `thiserror::Error` provides us.
+        self.log.borrow_mut().push(e.to_string());
+        // Then do the trivial mapping down to the flat enum.
+        match e {
+            RichError::InvalidArg { .. } => types::Errno::InvalidArg,
+            RichError::PicketLine { .. } => types::Errno::PicketLine,
+        }
+    }
+}
+
+impl<'a> one_error_conversion::OneErrorConversion for WasiCtx<'a> {
+    fn foo(&self, strike: u32, _s: &types::S) -> Result<types::T, RichError> {
+        // We use the argument to this function to exercise all of the
+        // possible error cases we could hit here
+        match strike {
+            0 => Ok(types::T {
+                f1: 123,
+                f2: 456.78,
+            }),
+            1 => Err(RichError::PicketLine(format!("I'm not a scab"))),
+            _ => Err(RichError::InvalidArg(format!("out-of-bounds: {}", strike))),
+        }
+    }
+}
+
+fn main() {
+    if std::env::var("RUST_LOG").is_err() {
+        // with no RUST_LOG env variable: use the tracing subscriber.
+        let subscriber = tracing_subscriber::fmt()
+            // all spans/events with a level equal to or higher than TRACE (e.g, trace, debug, info, warn, etc.)
+            // will be written to stdout.
+            .with_max_level(tracing::Level::TRACE)
+            // builds the subscriber.
+            .finish();
+        tracing::subscriber::set_global_default(subscriber).expect("set global tracing subscriber");
+    } else {
+        // with RUST_LOG set: use the env_logger backend to tracing.
+        env_logger::init();
+    }
+
+    let ctx = WasiCtx::new();
+    let host_memory = HostMemory::new();
+
+    // Exercise each of the branches in `foo`.
+    // Start with the success case:
+    let r0 = one_error_conversion::foo(&ctx, &host_memory, 0, 0, 8);
+    assert_eq!(
+        r0,
+        i32::from(types::Errno::Ok),
+        "Expected return value for strike=0"
+    );
+    assert!(ctx.log.borrow().is_empty(), "No error log for strike=0");
+
+    // First error case:
+    let r1 = one_error_conversion::foo(&ctx, &host_memory, 1, 0, 8);
+    assert_eq!(
+        r1,
+        i32::from(types::Errno::PicketLine),
+        "Expected return value for strike=1"
+    );
+    assert_eq!(
+        ctx.log.borrow_mut().pop().expect("one log entry"),
+        "Won't cross picket line: I'm not a scab",
+        "Expected log entry for strike=1",
+    );
+
+    // Second error case:
+    let r2 = one_error_conversion::foo(&ctx, &host_memory, 2, 0, 8);
+    assert_eq!(
+        r2,
+        i32::from(types::Errno::InvalidArg),
+        "Expected return value for strike=2"
+    );
+    assert_eq!(
+        ctx.log.borrow_mut().pop().expect("one log entry"),
+        "Invalid argument: out-of-bounds: 2",
+        "Expected log entry for strike=2",
+    );
+}
diff --git a/tests/spec_testsuite b/tests/spec_testsuite
index 4b7738e05..21a10bc59 160000
--- a/tests/spec_testsuite
+++ b/tests/spec_testsuite
@@ -1,1 +1,1 @@
-Subproject commit 4b7738e05b88869019b58de2db26a36149e237d0
+Subproject commit 21a10bc5942aeb328a057fa54820d3ffb5e7f144