mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 07:37:57 +00:00
Add a new host function for reporting fatal errors; make WASM backtraces readable when printing out errors (#10741)
* Add a new host function for reporting fatal errors * Fix one of the wasmtime executor tests * Have `#[runtime_interface(wasm_only)]` actually mean WASM-only, and not no_std-only * Print out errors through `Display` instead of `Debug` * Switch one more trait to require `Error` for its error instead of only `Debug` * Align to review comments
This commit is contained in:
@@ -22,7 +22,7 @@ mod sandbox;
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use hex_literal::hex;
|
||||
use sc_executor_common::{runtime_blob::RuntimeBlob, wasm_runtime::WasmModule};
|
||||
use sc_executor_common::{error::Error, runtime_blob::RuntimeBlob, wasm_runtime::WasmModule};
|
||||
use sc_runtime_test::wasm_binary_unwrap;
|
||||
use sp_core::{
|
||||
blake2_128, blake2_256, ed25519, map,
|
||||
@@ -122,7 +122,7 @@ fn call_in_wasm<E: Externalities>(
|
||||
call_data: &[u8],
|
||||
execution_method: WasmExecutionMethod,
|
||||
ext: &mut E,
|
||||
) -> Result<Vec<u8>, String> {
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
let executor =
|
||||
crate::WasmExecutor::<HostFunctions>::new(execution_method, Some(1024), 8, None, 2);
|
||||
executor.uncached_call(
|
||||
@@ -148,25 +148,16 @@ fn call_not_existing_function(wasm_method: WasmExecutionMethod) {
|
||||
let mut ext = TestExternalities::default();
|
||||
let mut ext = ext.ext();
|
||||
|
||||
match call_in_wasm(
|
||||
"test_calling_missing_external",
|
||||
&[],
|
||||
wasm_method,
|
||||
&mut ext,
|
||||
) {
|
||||
Ok(_) => panic!("was expected an `Err`"),
|
||||
Err(e) => {
|
||||
match wasm_method {
|
||||
WasmExecutionMethod::Interpreted => assert_eq!(
|
||||
&format!("{:?}", e),
|
||||
"\"Trap: Trap { kind: Host(Other(\\\"Function `missing_external` is only a stub. Calling a stub is not allowed.\\\")) }\""
|
||||
),
|
||||
match call_in_wasm("test_calling_missing_external", &[], wasm_method, &mut ext).unwrap_err() {
|
||||
Error::AbortedDueToTrap(error) => {
|
||||
let expected = match wasm_method {
|
||||
WasmExecutionMethod::Interpreted => "Trap: Host(Other(\"Function `missing_external` is only a stub. Calling a stub is not allowed.\"))",
|
||||
#[cfg(feature = "wasmtime")]
|
||||
WasmExecutionMethod::Compiled => assert!(
|
||||
format!("{:?}", e).contains("Wasm execution trapped: call to a missing function env:missing_external")
|
||||
),
|
||||
}
|
||||
}
|
||||
WasmExecutionMethod::Compiled => "call to a missing function env:missing_external"
|
||||
};
|
||||
assert_eq!(error.message, expected);
|
||||
},
|
||||
error => panic!("unexpected error: {:?}", error),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,25 +166,18 @@ fn call_yet_another_not_existing_function(wasm_method: WasmExecutionMethod) {
|
||||
let mut ext = TestExternalities::default();
|
||||
let mut ext = ext.ext();
|
||||
|
||||
match call_in_wasm(
|
||||
"test_calling_yet_another_missing_external",
|
||||
&[],
|
||||
wasm_method,
|
||||
&mut ext,
|
||||
) {
|
||||
Ok(_) => panic!("was expected an `Err`"),
|
||||
Err(e) => {
|
||||
match wasm_method {
|
||||
WasmExecutionMethod::Interpreted => assert_eq!(
|
||||
&format!("{:?}", e),
|
||||
"\"Trap: Trap { kind: Host(Other(\\\"Function `yet_another_missing_external` is only a stub. Calling a stub is not allowed.\\\")) }\""
|
||||
),
|
||||
match call_in_wasm("test_calling_yet_another_missing_external", &[], wasm_method, &mut ext)
|
||||
.unwrap_err()
|
||||
{
|
||||
Error::AbortedDueToTrap(error) => {
|
||||
let expected = match wasm_method {
|
||||
WasmExecutionMethod::Interpreted => "Trap: Host(Other(\"Function `yet_another_missing_external` is only a stub. Calling a stub is not allowed.\"))",
|
||||
#[cfg(feature = "wasmtime")]
|
||||
WasmExecutionMethod::Compiled => assert!(
|
||||
format!("{:?}", e).contains("Wasm execution trapped: call to a missing function env:yet_another_missing_external")
|
||||
),
|
||||
}
|
||||
}
|
||||
WasmExecutionMethod::Compiled => "call to a missing function env:yet_another_missing_external"
|
||||
};
|
||||
assert_eq!(error.message, expected);
|
||||
},
|
||||
error => panic!("unexpected error: {:?}", error),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,6 +469,7 @@ fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) {
|
||||
"test_exhaust_heap",
|
||||
&[0],
|
||||
)
|
||||
.map_err(|e| e.to_string())
|
||||
.unwrap_err();
|
||||
|
||||
assert!(err.contains("Allocator ran out of space"));
|
||||
@@ -691,7 +676,7 @@ fn panic_in_spawned_instance_panics_on_joining_its_result(wasm_method: WasmExecu
|
||||
let error_result =
|
||||
call_in_wasm("test_panic_in_spawned", &[], wasm_method, &mut ext).unwrap_err();
|
||||
|
||||
assert!(error_result.contains("Spawned task"));
|
||||
assert!(error_result.to_string().contains("Spawned task"));
|
||||
}
|
||||
|
||||
test_wasm_execution!(memory_is_cleared_between_invocations);
|
||||
@@ -789,3 +774,32 @@ fn take_i8(wasm_method: WasmExecutionMethod) {
|
||||
|
||||
call_in_wasm("test_take_i8", &(-66_i8).encode(), wasm_method, &mut ext).unwrap();
|
||||
}
|
||||
|
||||
test_wasm_execution!(abort_on_panic);
|
||||
fn abort_on_panic(wasm_method: WasmExecutionMethod) {
|
||||
let mut ext = TestExternalities::default();
|
||||
let mut ext = ext.ext();
|
||||
|
||||
match call_in_wasm("test_abort_on_panic", &[], wasm_method, &mut ext).unwrap_err() {
|
||||
Error::AbortedDueToPanic(error) => assert_eq!(error.message, "test_abort_on_panic called"),
|
||||
error => panic!("unexpected error: {:?}", error),
|
||||
}
|
||||
}
|
||||
|
||||
test_wasm_execution!(unreachable_intrinsic);
|
||||
fn unreachable_intrinsic(wasm_method: WasmExecutionMethod) {
|
||||
let mut ext = TestExternalities::default();
|
||||
let mut ext = ext.ext();
|
||||
|
||||
match call_in_wasm("test_unreachable_intrinsic", &[], wasm_method, &mut ext).unwrap_err() {
|
||||
Error::AbortedDueToTrap(error) => {
|
||||
let expected = match wasm_method {
|
||||
WasmExecutionMethod::Interpreted => "Trap: Unreachable",
|
||||
#[cfg(feature = "wasmtime")]
|
||||
WasmExecutionMethod::Compiled => "wasm trap: wasm `unreachable` instruction executed",
|
||||
};
|
||||
assert_eq!(error.message, expected);
|
||||
},
|
||||
error => panic!("unexpected error: {:?}", error),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ where
|
||||
allow_missing_host_functions: bool,
|
||||
export_name: &str,
|
||||
call_data: &[u8],
|
||||
) -> std::result::Result<Vec<u8>, String> {
|
||||
) -> std::result::Result<Vec<u8>, Error> {
|
||||
let module = crate::wasm_runtime::create_wasm_runtime_with_code::<H>(
|
||||
self.method,
|
||||
self.default_heap_pages,
|
||||
@@ -228,11 +228,10 @@ where
|
||||
allow_missing_host_functions,
|
||||
self.cache_path.as_deref(),
|
||||
)
|
||||
.map_err(|e| format!("Failed to create module: {:?}", e))?;
|
||||
.map_err(|e| format!("Failed to create module: {}", e))?;
|
||||
|
||||
let instance = module
|
||||
.new_instance()
|
||||
.map_err(|e| format!("Failed to create instance: {:?}", e))?;
|
||||
let instance =
|
||||
module.new_instance().map_err(|e| format!("Failed to create instance: {}", e))?;
|
||||
|
||||
let mut instance = AssertUnwindSafe(instance);
|
||||
let mut ext = AssertUnwindSafe(ext);
|
||||
@@ -243,7 +242,6 @@ where
|
||||
instance.call_export(export_name, call_data)
|
||||
})
|
||||
.and_then(|r| r)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,6 +279,7 @@ where
|
||||
"Core_version",
|
||||
&[],
|
||||
)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,12 +455,18 @@ impl RuntimeSpawn for RuntimeInstanceSpawn {
|
||||
// pool of instances should be used.
|
||||
//
|
||||
// https://github.com/paritytech/substrate/issues/7354
|
||||
let mut instance =
|
||||
module.new_instance().expect("Failed to create new instance from module");
|
||||
let mut instance = match module.new_instance() {
|
||||
Ok(instance) => instance,
|
||||
Err(error) =>
|
||||
panic!("failed to create new instance from module: {}", error),
|
||||
};
|
||||
|
||||
instance
|
||||
match instance
|
||||
.call(InvokeMethod::TableWithWrapper { dispatcher_ref, func }, &data[..])
|
||||
.expect("Failed to invoke instance.")
|
||||
{
|
||||
Ok(result) => result,
|
||||
Err(error) => panic!("failed to invoke instance: {}", error),
|
||||
}
|
||||
});
|
||||
|
||||
match result {
|
||||
@@ -471,7 +476,7 @@ impl RuntimeSpawn for RuntimeInstanceSpawn {
|
||||
Err(error) => {
|
||||
// If execution is panicked, the `join` in the original runtime code will
|
||||
// panic as well, since the sender is dropped without sending anything.
|
||||
log::error!("Call error in spawned task: {:?}", error);
|
||||
log::error!("Call error in spawned task: {}", error);
|
||||
},
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -105,13 +105,13 @@ impl VersionedRuntime {
|
||||
if new_inst {
|
||||
log::warn!(
|
||||
target: "wasm-runtime",
|
||||
"Fresh runtime instance failed with {:?}",
|
||||
"Fresh runtime instance failed with {}",
|
||||
e,
|
||||
)
|
||||
} else {
|
||||
log::warn!(
|
||||
target: "wasm-runtime",
|
||||
"Evicting failed runtime instance: {:?}",
|
||||
"Evicting failed runtime instance: {}",
|
||||
e,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user