调试交易

(还请参阅本页面的相关页面:the Debugger Tour

有两种方法可以开始调试会话,每一种都对应着不同的用例。

  • 用例1:用于调试在Remix中进行的交易 - 在Remix终端中的交易日志中单击Debug按钮

  • 用例2:用于调试交易,其中您拥有来自已验证合约交易哈希或者您拥有与部署的合约具有相同编译设置的交易哈希已编译源代码

从终端的事务日志中启动调试

让我们从一份基本合约开始(或者自己替换这个合约):

pragma solidity >=0.5.1 <0.6.0;
contract Donation {
    address owner;
    event fundMoved(address _to, uint _amount);
    modifier onlyowner { if (msg.sender == owner) _; }
    address[] _giver;
    uint[] _values;

    constructor() public {
        owner = msg.sender;
    }

    function donate() public payable {
        addGiver(msg.value);
    }

    function moveFund(address payable _to, uint _amount) onlyowner  public {
        uint balance = address(this).balance;
        uint amount = _amount;
        if (amount <= balance) {
            if (_to.send(balance)) {
                emit fundMoved(_to, amount);    
            } else {
               revert();
            }
        } else {
            revert();
        }
    }

    function addGiver(uint _amount) internal {
        _giver.push(msg.sender);
        _values.push(_amount);
    }
}
  • 在Remix中创建一个新文件,然后将上面的代码复制到其中。

  • 编译代码。

  • 前往 Run & Deploy 模块。

在本教程中,我们将运行Remix VM

  • 部署合约:

点击Deploy按钮。

您将看到已部署的实例(又名 udapp)。

然后打开它(通过单击插入符号)。

我们将调用 Donate 函数向这个合约发送2个ETH。

要完成此操作:在数值输入框中输入2,并选择以太作为单位(不要将默认单位留为gwei,否则更改将很难检测)。

然后点击Donate按钮。

这会将ETH发送到这个合约。

因为我们使用的是 “Remix虚拟机”,所以一切都几乎瞬时发生。(如果我们使用的是 “Injected Web 3”,那么我们需要批准交易、支付燃料费,并等待交易被挖掘。)

Remix 在终端中显示与每个交易结果相关的信息。

Remix在终端中显示与每个交易结果相关的信息。

单击debug按钮.

但在我们进入实际的调试工具之前,下一节将展示如何直接从调试器开始一个调试会话。

从调试器启动调试

单击图标面板中的错误图标以访问侧面板中的调试器。

如果您没有看到错误图标,请转到插件管理器并激活调试器。

您可以通过提供交易哈希来启动调试会话。

要查找交易哈希:

  1. 在终端中进行交易。

  2. 点击带有交易的行 - 以展开日志。

  3. 交易哈希在那里 - 复制它。

然后在调试器中粘贴哈希并点击开始调试按钮。

使用调试器

调试器允许人们查看有关事务执行的详细信息。 它使用编辑器在源代码中显示当前执行的位置。

导航部分包含一个滑块和按钮,可用于逐步执行事务。

调试器按钮功能说明

  1. Step Over Back返回到上一步,但忽略/跳过函数调用:调试器不会进入函数

  2. Step Back 返回到上一步。不会忽略函数调用:调试器将进入沿途的任何函数

  3. Step Into 进入下一步。不会忽略函数调用:调试器将会进入经过的任何函数

  4. Step Over Forward 向前执行下一步,但忽略/跳过函数调用:调试器不会进入函数

  5. Jump to the Previous Breakpoint 将调试器转移到上一个访问的断点。请注意,可以通过单击源代码中的行号设置断点。

  6. Jump Out 将调试器转移到函数的结尾

  7. Jump to the Next Breakpoint 将调试器转移到下一个断点

11 个面板提供了有关执行的详细信息:

指令

指令面板显示当前执行合约的字节码——当前步骤突出显示。

重要提示:当此面板被隐藏时,滑块将具有更粗的粒度,并且仅在表达式边界处停止,即使它们编译为多个EVM指令。当该面板显示时,将可以逐步执行每个指令,甚至是那些引用相同表达式的指令。

Solidity Locals

Solidity Locals 面板显示与当前上下文相关的局部变量。

Solidity State

Solidity State 面板显示当前执行合约的状态变量。

低级面板

这些面板显示有关执行的低级信息:

  • 堆栈

  • 存储更改

  • 内存

  • 调用数据

  • 调用堆栈

  • Return Valu(仅当当前步骤是返回操作码时)

  • 完整存储更改(仅在执行结束时,并显示所有存储更改)

恢复交易

一笔交易可能会被reverted(因为GAS不足异常、Solidity 的 revert 语句或低级别的异常)。

了解异常并找出异常在源代码中的位置很重要。

Remix 会在执行抛出异常时发出警告。warning 按钮将跳转到异常发生前的最后一个操作码。

断点

导航区域的最后两个按钮用于跳回上一个断点或前进到下一个断点。

断点可以通过在编辑器中单击行号来添加和删除。

使用带有断点的调试会话时,执行将跳转到第一个遇到的断点。

重要提示: 如果您在声明变量的行上添加断点,它可能会被触发两次:一次用于将变量初始化为零,另一次用于分配实际值。

下面是这个问题的一个例子。如果您正在调试以下合约:

pragma solidity >=0.5.1 <0.6.0;

contract ctr {
    function hid() public {
        uint p = 45;
        uint m;
        m = 89;
        uint l = 34;
    }
}

断点已设置在这些行上。

uint p = 45;

m = 89;

uint l = 34;

然后点击Jump to the next breakpoint按钮将按照给定的顺序停止在以下行:

uint p = 45; (声明p)

uint l = 34; (声明l)

uint p = 45; (p赋值45)

`m = 89; (m赋值89)

`uint l = 34; (l赋值34)