Skip to main content

Europa's Wasm Backtrace

Background Information#

The execution of pallet-contracts includes execution in the contract model and execution in Wasm. The execution process in the contract model is transferred to the pallet-contracts module through Wasm's host_function. If panic or incorrect positioning occurs, the runtime of the node can be positioned in the form of native operation. Since the execution process in Wasm is in the Wasm virtual machine, it is a black box to the outside world. If the internal execution process crashes abnormally, it can only be displayed by the Wasm executor.

Europa's pallet-contracts module currently supports the following two types of actuators:

  • wasmi: Wasm interpreter developed by the Parity. When Wasm executes panic, it will only return an error without Backtrace. Patract forks the official wasmi, and adds tracking and printing of the execution stack on top of the original. When a panic occurs during Wasm's execution, the current execution stack and corresponding information will be returned with an error.
  • wasmtime: Wasm's JIT executor, which comes with Backtrace when it crashes.

The conditins of Europa can print out Wasm Backtrace#

Wasm can print Backtrace, requiring the name section in the Wasm file compiled by the contract. Since cargo-contract provided by Parity encapsulated many operations, the current default operation is to compile the contract in the best optimized way, and the name section section will be removed in this process. On the other hand, cargo-contract does not provide corresponding interfaces or options that allow you to adjust the optimization conditions used in contract compilation and whether to retain some debugging information. Therefore, Patract can only provide a modified version of cargo-contract, and you can use this modified version of cargo-contract to compile a contract Wasm file with a name section.

On the other hand, the original code will be optimized during the compilation of the release, and it may be disturbed to locate the problem through the optimized Backtrace. Therefore, it is best to reduce the optimization level so that the Backtrace at the time of a crash will most likely match the original code.

Install cargo-contract under Patract repository#

  • Install cargo-contract under Patract repository.
    $ cargo install cargo-contract --git https://github.com/patractlabs/cargo-contract --branch=v0.12.1 --force

Note Since the current version of Parity's cargo-contract is v0.12.1 , our Patract has added features based on this version. If cargo-contract continues to be upgraded in the future, Patract will continue to be maintained.

The cargo-contract installed in this way will overwrite the installed cargo-contract. Therefore, please pay attention to which repository the cargo-contract in the current environment comes from to prevent interference when locating problems.

Execute the following command,The results listed can be used to judge what source the cargo-contract installation in the current environment comes from. For example, the following result is from Patract. If there is no parenthesis and the content in it, it means it is from crates.io.

$ cargo install --list | grep cargo-contractcargo-contract v0.12.1 (https://github.com/patractlabs/cargo-contract.git?branch=tag-v0.12.1#0d682762):cargo-contract
  • If you have installed the official cargo-contract and does not want to overwrite the installation, you can use manual compilation.
    $ git clone https://github.com/patractlabs/cargo-contract --branch=tag-v0.12.1$ cd cargo-contract$ cargo build --release

After compilation, you can move the compiled product to any path that can be accessed globally, and rename it ,in case it conflicts with the installed cargo-contract.

$ cp target/release/cargo-contract <to any path>/patract-cargo-contract

In the subsequent compilation of the ink! contract, use patract-cargo-contract xxx instead of cargo +nighlty contract xxx to execute the corresponding commands,but this method requires the default toolchain to be nightly)

Use Patract's cargo-contract to generate*.wasm/*.contractfiles withname sectionsection#

Patract's cargo-contract provides -d/--debug options. When the following command is executed,The generated *.wasm/*.contract file is consistent with parity's official cargo-contract execution result.

$ cargo +nightly contract build

When the following command is executed,The generated *.wasm/*.contract file is the *.wasm/*.contract file that is not optimized and carries the name section section. It is equivalent to the files generated in this way replace the files generated by the original generation logic.

$ cargo +nightly contract build --debug

Note The size of the compiled product generated by this way is generally several hundred times the size of the original product. Therefore, the developer can pay attention to the size of the generated product to roughly determine the compiled product generated by which compilation method.

The example is as follows.

$ cd target/ink$ ls -h-rw-rw-r-- 1 root root 1.5M 312 16:01 flipper.contract-rw-rw-r-- 1 root root 6.1K 312 15:34 flipper.contract.old-rw-rw-r-- 1 root root 732K 312 16:00 flipper.wasm-rw-rw-r-- 1 root root 2.5K 312 15:34 flipper.wasm.old-rw-rw-r-- 1 root root 2.1K 312 16:01 metadata.json

The file with *.old means it was generated by the parity version of cargo-contract( renamed after the first compilation), on the contrary, the file with the same name is from Patract's cargo-contract with the addition of -- The debug command is generated. You can see that the new file is many times larger than the old file. And metadata.json is unchanged.

Wasm Backtrace description#

To be completed.

Experimental functions#

Wasm Backtrace print line number (only Wasmtime is supported)#

TODO: This part is not completed

Add WASMTIME_BACKTRACE_DETAILS=1 when starting Europa or set this variable as an environment variable

WASMTIME_BACKTRACE_DETAILS=1 europa --tmp# or useexport WASMTIME_BACKTRACE_DETAILS=1europa --tmp # run europa in normal way

Then in the wasm_error section of Europa's log, the line number in the original code corresponding to the crash stack will appear.

wasm_error: Error::Trap(    Trap {        code: TrapCode::UnreachableCodeReached,        trace: [            "wasm trap: unreachable",            "wasm backtrace:",            "    0: 0x31b2 - <unknown>!core::panicking::panic::he000af669cfcac01",            "    1: 0x3c8c - <unknown>!flipper::flippter::_::<impl flipper::flippter::Flippter>::flip::h12b84979a77ae484",            "    2: 0x10fa - core::result::Result<T,E>::map_err::h576871030fe833d4",            "                    at /home/clearloop/.cargo/registry/src/github.com-1ecc6299db9ec823/官方-scale-codec-2.0.1/src/codec.rs:1199:31",            "    3: 0x10d6 - core::result::Result<T,E>::map_err::h576871030fe833d4",            "                    at /home/clearloop/.cargo/registry/src/github.com-1ecc6299db9ec823/官方-scale-codec-2.0.1/src/codec.rs:1198",            "    4: 0x3966 - <unknown>!<flipper::flippter::_::_::__ink_MessageDispatchEnum as ink_lang::dispatcher::Execute>::execute::{{closure}}::hf35b139aaf5fba3b",            "    5: 0x3941 - <unknown>!ink_lang::dispatcher::execute_message_mut::hf62eb790d230d371",            "    6: 0x3c12 - <unknown>!<flipper::flippter::_::_::__ink_MessageDispatchEnum as ink_lang::dispatcher::Execute>::execute::heae3e5bbfc02afa0",            "    7: 0x3a7a - <unknown>!flipper::flippter::_::<impl ink_lang::contract::DispatchUsingMode for flipper::flippter::Flippter>::dispatch_using_mode::h8e0c4495e09cd910",            "    8: 0x3ba3 - <unknown>!call",            "    9: 0xf704 - <unknown>!<wasm function 638>",            "",        ],    },),

In this backtrace log, some parts that can parse the line number will be appended with the line number corresponding to the function in the error stack at the end of that line,The example is as follows.

"2: 0x10fa - core::result::Result<T,E>::map_err::h576871030fe833d4",            "                    at /home/clearloop/.cargo/registry/src/github.com-1ecc6299db9ec823/parity-scale-codec-2.0.1/src/codec.rs:1199:31"

The codec.rs:1199:31 part means that this frame in the error stack corresponds to codec.rs. The line number of the file is 1199 and the column number is 31. The remaining lines do not have line numbers due to insufficient parsing or because the code is generated by macros.