Substrate Developer Hub

Substrate Developer Hub

  • 教程
  • 知识库
  • 进阶菜谱
  • API 文档
  • Languages icon简体中文
    • English
    • 协助翻译

›Runtime

开始

  • 总览
  • 安装
  • 在 Windows 系统开始
  • 词汇表

学习 Substrate

  • Extrinsics
  • 交易池
  • 账户摘要
  • 会话密钥
  • 交易权重
  • 链下功能

Runtime

  • Runtime 总览
  • Runtime 的基本类型
  • FRAME
  • Pallets
  • Runtime宏
  • Runtime 元数据
  • Runtime 存储
  • Runtime 来源
  • Runtime 执行流程
  • Runtime事件
  • Runtime 错误
  • 交易费用
  • 链下工作机
  • 调试
  • Runtime 测试
  • 链上随机生成
  • Runtime 升级

智能合约

  • 总览
  • ink! 智能合约
  • ink! 概念
  • ink! 开发
  • EVM 模块
  • ink! 常问问题

整合

  • Polkadot-JS
  • 客户端库
  • 链规范
  • Subkey 工具
  • 内存分析

进阶

  • SCALE 编解码器
  • 共识机制
  • 区块导入过程
  • 执行器
  • 密码学
  • 存储
  • SS58 地址格式

贡献

  • 协助翻译
Translate

调试

在所有软件开发中,调试都是必不可少的,区块链也不例外。 大部分用于通用目的的Rust调试工具,也同样适用于Substrate。 但是,由于Substrate runtime是在no_std环境中运行的,进行调试时会有一些限制。

FRAME的调试 & 日志工具

The FRAME Support debug module contains macros and functions that make it possible to print logs out of runtime code.

从Native Runtime打印日志

For performance-preserving, native-only debugging, use the macros in the frame_support::debug::native module.

pub fn do_something(origin) -> DispatchResult {
    print("Execute do_something");

    let who = ensure_signed(origin)?;
    let my_val: u32 = 777;

    Something::put(my_val);

    frame_support::debug::native::debug!("called by {:?}", who);

    Self::deposit_event(RawEvent::SomethingStored(my_val, who));
    Ok(())
}

The frame_support::debug::native::debug! macro avoids extra overhead in Wasm runtimes, but is only effective when the runtime is executing in native mode. 为了查看这些日志消息,必须给节点配置正确的日志目标。 默认情况下,日志目标的名称与包含了日志消息的crate的名称保持一致。

./target/release/node-template --dev -lpallet_template=debug

debug!宏接收了一个可选参数target,于指定日志目标名称。

frame_support::debug::native::debug!(target: "customTarget", "called by {:?}", who);

从Wasm Runtime打印日志

可以通过牺牲性能以换取Wasm runtime输出日志记录。

pub fn do_something(origin) -> DispatchResult {
    print("Execute do_something");

    let who = ensure_signed(origin)?;
    let my_val: u32 = 777;

    Something::put(my_val);

    frame_support::debug::RuntimeLogger::init();
    frame_support::debug::debug!("called by {:?}", who);

    Self::deposit_event(RawEvent::SomethingStored(my_val, who));
    Ok(())
}

Printable Trait

Printable trait提供了runtime在no_std 和std两种环境中进行打印的一种方式。 The print function works with any type that implements the <a href="https://substrate.dev/rustdocs/v3.0.0/sp_runtime/traits/trait. Substrate默认对某些类型 (u8, u32, u64, usize, &[u8], &str) 实现了此特征。 当然也可把它应用到自定义类型上。 以下示例用node-template作为代码样版,在一个pallet的Error类型上实现了printable trait。

use sp_runtime::traits::Printable;
use sp_runtime::print;
// The pallet's errors
decl_error! {
    pub enum Error for Module<T: Config> {
        /// Value was None
        NoneValue,
        /// Value reached maximum and cannot be incremented further
        StorageOverflow,
    }
}

impl<T: Config> Printable for Error<T> {
    fn print(&self) {
        match self {
            Error::NoneValue => "Invalid Value".print(),
            Error::StorageOverflow => "Value Exceeded and Overflowed".print(),
            _ => "Invalid Error Case".print(),
        }
    }
}
/// takes no parameters, attempts to increment storage value, and possibly throws an error
pub fn cause_error(origin) -> dispatch::DispatchResult {
    // Check it was signed and get the signer. See also: ensure_root and ensure_none
    let _who = ensure_signed(origin)?;

    print("My Test Message");

    match Something::get() {
        None => {
            print(Error::<T>::NoneValue);
            Err(Error::<T>::NoneValue)?
        }
        Some(old) => {
            let new = old.checked_add(1).ok_or(
                {
                    print(Error::<T>::StorageOverflow);
                    Error::<T>::StorageOverflow
                })?;
            Something::put(new);
            Ok(())
        },
    }
}

使用RUST_LOG环境变量来运行节点二进制文件以打印值。

RUST_LOG=runtime=debug ./target/release/node-template --dev

每次调用runtime函数时,值都会打印在终端或标准输出中。

2020-01-01 tokio-blocking-driver DEBUG runtime  My Test Message  <-- str implements Printable by default
2020-01-01 tokio-blocking-driver DEBUG runtime  Invalid Value    <-- the custom string from NoneValue
2020-01-01 tokio-blocking-driver DEBUG runtime  DispatchError
2020-01-01 tokio-blocking-driver DEBUG runtime  8
2020-01-01 tokio-blocking-driver DEBUG runtime  0                <-- index value from the Error enum definition
2020-01-01 tokio-blocking-driver DEBUG runtime  NoneValue        <-- str which holds the name of the ident of the error

重要说明:向runtime添加多个打印函数会导致二进制文件和wasm blob变得更大,而这些增加的调试代码在生产环境中是用不上的。

Substrate自带的print函数

对于传统用例,Substrate提供了额外的工具来进行Print 调试(或跟踪)。 You can use the print function to log the status of the runtime execution.

use sp_runtime::print;

// --snip--
pub fn do_something(origin) -> DispatchResult {
    print("Execute do_something");

    let who = ensure_signed(origin)?;
    let my_val: u32 = 777;

    Something::put(my_val);

    print("After storing my_val");

    Self::deposit_event(RawEvent::SomethingStored(my_val, who));
    Ok(())
}
// --snip--

可使用RUST_LOG 环境变量启动区块链,以查看打印日志。

RUST_LOG=runtime=debug ./target/release/node-template --dev

如果触发了错误,这些值将打印在终端或标准输出中。

2020-01-01 00:00:00 tokio-blocking-driver DEBUG runtime  Execute do_something
2020-01-01 00:00:00 tokio-blocking-driver DEBUG runtime  After storing my_val

If Std

传统的print 函数允许您打印并实现Printable trait。 但是,在某些传统用例下,您可能想要做的不仅仅是打印,或者不想仅仅出于调试目的而去麻烦地使用Substrate的一些traits。 The if_std! macro is useful for this situation.

使用此宏时要注意,仅当真正运行的是runtime的native版本时,宏内部代码才会执行。

use sp_std::if_std; // Import into scope the if_std! macro.

println! 语句应放置在if_std宏内。

decl_module! {

        // --snip--
        pub fn do_something(origin) -> DispatchResult {

            let who = ensure_signed(origin)?;
            let my_val: u32 = 777;

            Something::put(my_val);

            if_std! {
                // This code is only being compiled and executed when the `std` feature is enabled.
                println!("Hello native world!");
                println!("My value is: {:#?}", my_val);
                println!("The caller account is: {:#?}", who);
            }

            Self::deposit_event(RawEvent::SomethingStored(my_val, who));
            Ok(())
        }
        // --snip--
}

每次调用runtime函数时,值都会打印在终端或标准输出中。

$       2020-01-01 00:00:00 Substrate Node
        2020-01-01 00:00:00   version x.y.z-x86_64-linux-gnu
        2020-01-01 00:00:00   by Anonymous, 2017, 2020
        2020-01-01 00:00:00 Chain specification: Development
        2020-01-01 00:00:00 Node name: my-node-007
        2020-01-01 00:00:00 Roles: AUTHORITY
        2020-01-01 00:00:00 Imported 999 (0x3d7a…ab6e)
        # --snip--
->      Hello native world!
->      My value is: 777
->      The caller account is: d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d (5GrwvaEF...)
        # --snip--
        2020-01-01 00:00:00 Imported 1000 (0x3d7a…ab6e)

← 链下工作机Runtime 测试 →
  • FRAME的调试 & 日志工具
    • 从Native Runtime打印日志
    • 从Wasm Runtime打印日志
  • Printable Trait
  • Substrate自带的print函数
  • If Std
Substrate Developer Hub
开发者中心
教程知识库进阶菜谱API 文档
社区
社区主页通讯Substrate 技术聊天室Substrate 研讨会Stack Overflow推特聚会活动
更多
Substrate Builders 计划BlogSubstrate GitHub开发者中心 GitHub隐私政策使用条款Cookie 设置
Copyright © 2021 Parity Technologies