Repository: bytecodealliance/wasmtime
Pull request #8026 (2024-02-28 23:52:17)

Show true error reason in `wasmtime serve`
The `wasmtime serve` command prints failed HTTP handling to `stderr` but, when the reason was due to failure inside the HTTP handler itself, the printed error message is not descriptive enough. After looking at long dumps of `guest never invoked `response-outparam::set` method`, I realized that errors inside the Tokio `task` are never propagated outwards. This change captures those errors and appends them to the printed error message.

<!--
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/Cargo.lock b/Cargo.lock
index 1699a0744..def959fc7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -333,9 +333,9 @@ dependencies = [
 
 [[package]]
 name = "capstone"
-version = "0.9.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "178b3c80b4ce3939792d1dd6600575e012da806a10e81abe812dc29cbfe629ec"
+checksum = "b08ca438d9585a2b216b0c2e88ea51e096286c5f197f7be2526bb515ef775b6c"
 dependencies = [
  "capstone-sys",
  "libc",
@@ -343,9 +343,9 @@ dependencies = [
 
 [[package]]
 name = "capstone-sys"
-version = "0.13.0"
+version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf2f3d97b88fa4a9a6464c7c08b8c4588aaea8c18d0651eca11f2ca15f50f1f6"
+checksum = "fe7183271711ffb7c63a6480e4baf480e0140da59eeba9b18fcc8bf3478950e3"
 dependencies = [
  "cc",
  "libc",
diff --git a/Cargo.toml b/Cargo.toml
index a3f26a5f1..6412d17a7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -251,7 +251,7 @@ env_logger = "0.10"
 log = { version = "0.4.8", default-features = false }
 clap = { version = "4.3.12", default-features = false, features = ["std", "derive"] }
 hashbrown = { version = "0.14", default-features = false }
-capstone = "0.9.0"
+capstone = "0.12.0"
 once_cell = "1.12.0"
 smallvec = { version = "1.6.1", features = ["union"] }
 tracing = "0.1.26"
diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs
index 2b03d21b7..821636a53 100644
--- a/crates/cli-flags/src/lib.rs
+++ b/crates/cli-flags/src/lib.rs
@@ -81,6 +81,26 @@ wasmtime_option_group! {
         /// Configure attempting to initialize linear memory via a
         /// copy-on-write mapping (default: yes)
         pub memory_init_cow: Option<bool>,
+
+        /// The maximum number of WebAssembly instances which can be created
+        /// with the pooling allocator.
+        pub pooling_total_core_instances: Option<u32>,
+
+        /// The maximum number of WebAssembly components which can be created
+        /// with the pooling allocator.
+        pub pooling_total_component_instances: Option<u32>,
+
+        /// The maximum number of WebAssembly memories which can be created with
+        /// the pooling allocator.
+        pub pooling_total_memories: Option<u32>,
+
+        /// The maximum number of WebAssembly tables which can be created with
+        /// the pooling allocator.
+        pub pooling_total_tables: Option<u32>,
+
+        /// The maximum number of WebAssembly stacks which can be created with
+        /// the pooling allocator.
+        pub pooling_total_stacks: Option<u32>,
     }
 
     enum Optimize {
@@ -528,6 +548,21 @@ impl CommonOptions {
                     if let Some(size) = self.opts.pooling_table_keep_resident {
                         cfg.table_keep_resident(size);
                     }
+                    if let Some(limit) = self.opts.pooling_total_core_instances {
+                        cfg.total_core_instances(limit);
+                    }
+                    if let Some(limit) = self.opts.pooling_total_component_instances {
+                        cfg.total_component_instances(limit);
+                    }
+                    if let Some(limit) = self.opts.pooling_total_memories {
+                        cfg.total_memories(limit);
+                    }
+                    if let Some(limit) = self.opts.pooling_total_tables {
+                        cfg.total_tables(limit);
+                    }
+                    if let Some(limit) = self.opts.pooling_total_stacks {
+                        cfg.total_stacks(limit);
+                    }
                     if let Some(enable) = self.opts.memory_protection_keys {
                         if enable {
                             cfg.memory_protection_keys(wasmtime::MpkEnabled::Enable);
diff --git a/src/commands/serve.rs b/src/commands/serve.rs
index f11f6bc73..85e2a40c5 100644
--- a/src/commands/serve.rs
+++ b/src/commands/serve.rs
@@ -385,8 +385,7 @@ async fn handle_request(
 
     let (sender, receiver) = tokio::sync::oneshot::channel();
 
-    // TODO: need to track the join handle, but don't want to block the response on it
-    tokio::task::spawn(async move {
+    let task = tokio::task::spawn(async move {
         let req_id = inner.next_req_id();
         let (mut parts, body) = req.into_parts();
 
@@ -450,7 +449,20 @@ async fn handle_request(
     match receiver.await {
         Ok(Ok(resp)) => Ok(resp),
         Ok(Err(e)) => Err(e.into()),
-        Err(_) => bail!("guest never invoked `response-outparam::set` method"),
+        Err(_) => {
+            // An error in the receiver (`RecvError`) only indicates that the
+            // task exited before a response was sent (i.e., the sender was
+            // dropped); it does not describe the underlying cause of failure.
+            // Instead we retrieve and propagate the error from inside the task
+            // which should more clearly tell the user what went wrong. Note
+            // that we assume the task has already exited at this point so the
+            // `await` should resolve immediately.
+            let e = match task.await {
+                Ok(r) => r.expect_err("if the receiver has an error, the task must have failed"),
+                Err(e) => e.into(),
+            };
+            bail!("guest never invoked `response-outparam::set` method: {e:?}")
+        }
     }
 }
 
diff --git a/supply-chain/config.toml b/supply-chain/config.toml
index 685b1ca91..455588585 100644
--- a/supply-chain/config.toml
+++ b/supply-chain/config.toml
@@ -205,11 +205,11 @@ version = "1.1.0"
 criteria = "safe-to-deploy"
 
 [[exemptions.capstone]]
-version = "0.9.0"
+version = "0.12.0"
 criteria = "safe-to-deploy"
 
 [[exemptions.capstone-sys]]
-version = "0.13.0"
+version = "0.16.0"
 criteria = "safe-to-deploy"
 
 [[exemptions.cast]]


Test patch:
diff --git a/winch/filetests/src/disasm.rs b/winch/filetests/src/disasm.rs
index 5c921d041..a140f2b8e 100644
--- a/winch/filetests/src/disasm.rs
+++ b/winch/filetests/src/disasm.rs
@@ -33,6 +33,7 @@ pub fn disasm(
 
             let is_jump = detail
                 .groups()
+                .iter()
                 .find(|g| g.0 as u32 == CS_GRP_JUMP)
                 .is_some();
 
@@ -67,8 +68,12 @@ pub fn disasm(
 
             // Flip write_offsets to true once we've seen a `ret`, as instructions that follow the
             // return are often related to trap tables.
-            write_offsets =
-                write_offsets || detail.groups().find(|g| g.0 as u32 == CS_GRP_RET).is_some();
+            write_offsets = write_offsets
+                || detail
+                    .groups()
+                    .iter()
+                    .find(|g| g.0 as u32 == CS_GRP_RET)
+                    .is_some();
 
             line
         })