go-ethereum/docs/_evm-tracing/builtin-tracers.md
Sina Mahmoodi 761fcd5965
docs: update tracing docs (#26270)
* Move JS tracing ref from API page

* add tracerConfig to ref, link to traceConfig in other methods
2022-11-29 09:51:29 +01:00

515 lines
19 KiB
Markdown

---
title: Built-in tracers
sort_key: C
---
Geth comes bundled with a choice of tracers that can be invoked via the
[tracing API](/docs/rpc/ns-debug). Some of these built-in tracers are implemented
natively in Go, and others in Javascript. The default tracer is the opcode logger
(otherwise known as struct logger) which is the default tracer for all the methods.
Other tracers have to be specified by passing their name to the `tracer` parameter
in the API call.
* TOC
{:toc}
## Struct/opcode logger
The struct logger (aka opcode logger) is a native Go tracer which executes a
transaction and emits the opcode and execution context at every step. This is
the tracer that will be used when no name is passed to the API, e.g.
`debug.traceTransaction(<txhash>)`. The following information is emitted at each step:
| field | type | description |
| ---------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| pc | uint64 | program counter |
| op | byte | opcode to be executed |
| gas | uint64 | remaining gas |
| gasCost | uint64 | cost for executing op |
| memory | []byte | EVM memory. Enabled via `enableMemory` |
| memSize | int | Size of memory |
| stack | []uint256 | EVM stack. Disabled via `disableStack` |
| returnData | []byte | Last call's return data. Enabled via `enableReturnData` |
| storage | map[hash]hash | Storage slots of current contract read from and written to. Only emitted for `SLOAD` and `SSTORE`. Disabled via `disableStorage` |
| depth | int | Current call depth |
| refund | uint64 | Refund counter |
| error | string | Error message if any |
Note that the fields `memory`, `stack`, `returnData`, and `storage` have dynamic
size and depending on the exact transaction they could grow large in size. This
is specially true for `memory` which could blow up the trace size. It is recommended
to keep them disabled unless they are explicitly required for a given use-case.
It is also possible to configure the trace by passing Boolean (true/false) values
for four parameters that tweak the verbosity of the trace. By default, the
_EVM memory_ and _Return data_ are not reported but the _EVM stack_ and
_EVM storage_ are. To report the maximum amount of data:
```shell
enableMemory: true
disableStack: false
disableStorage: false
enableReturnData: true
```
An example call:
```js
debug.traceTransaction('0xfc9359e49278b7ba99f59edac0e3de49956e46e530a53c15aa71226b7aa92c6f', {
enableMemory: true,
disableStack: false,
disableStorage: false,
enableReturnData: true
});
```
Return:
```terminal
{
"gas":25523,
"failed":false,
"returnValue":"",
"structLogs":[
{
"pc":0,
"op":"PUSH1",
"gas":64580,
"gasCost":3,
"depth":1,
"error":null,
"stack":[
],
"memory":null,
"storage":{
}
},
{
"pc":2,
"op":"PUSH1",
"gas":64577,
"gasCost":3,
"depth":1,
"error":null,
"stack":[
"0000000000000000000000000000000000000000000000000000000000000060"
],
"memory":null,
"storage":{
}
},
...
```
## Native tracers
The following tracers are implement in Go. This means they are much more performant
than other tracers that are written in Javascript. The tracers are selected by
passing their name to the `tracer` parameter when invoking a tracing API method,
e.g. `debug.traceTransaction(<txhash>, { tracer: 'callTracer' })`.
### 4byteTracer
Solidity contract functions are
[addressed](https://docs.soliditylang.org/en/develop/abi-spec.html#function-selector)
using the first four four byte of the Keccak-256 hash of their signature. Therefore
when calling the function of a contract, the caller must send this function selector
as well as the ABI-encoded arguments as call data.
The `4byteTracer` collects the function selectors of every function executed in the
lifetime of a transaction, along with the size of the supplied call data. The result
is a `map[string]int` where the keys are `SELECTOR-CALLDATASIZE` and the values
are number of occurances of this key. For example:
Example call:
```sh
debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"})
```
Return:
```terminal
{
"0x27dc297e-128": 1,
"0x38cc4831-0": 2,
"0x524f3889-96": 1,
"0xadf59f99-288": 1,
"0xc281d19e-0": 1
}
```
### callTracer
The `callTracer` tracks all the call frames executed during a transaction, including depth 0.
The result will be a nested list of call frames, resembling how EVM works. They form a tree
with the top-level call at root and sub-calls as children of the higher levels. Each call
frame has the following fields:
| field | type | description |
| ------------ | ----------- | ------------------------------------ |
| type | string | CALL or CREATE |
| from | string | address |
| to | string | address |
| value | string | hex-encoded amount of value transfer |
| gas | string | hex-encoded gas provided for call |
| gasUsed | string | hex-encoded gas used during call |
| input | string | call data |
| output | string | return data |
| error | string | error, if any |
| revertReason | string | Solidity revert reason, if any |
| calls | []callframe | list of sub-calls |
Example Call:
```sh
> debug.traceTransaction("0x44bed3dc0f584b2a2ab32f5e2948abaaca13917eeae7ae3b959de3371a6e9a95", {tracer: 'callTracer'})
```
Return:
```terminal
{
calls: [{
from: "0xc8ba32cab1757528daf49033e3673fae77dcf05d",
gas: "0x18461",
gasUsed: "0x60",
input: "0x000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a6",
output: "0x557904b74478f8810cc02198544a030d1829bb491e14fe1dd0354e933c5e87bd",
to: "0x0000000000000000000000000000000000000002",
type: "STATICCALL"
}, {
from: "0xc8ba32cab1757528daf49033e3673fae77dcf05d",
gas: "0x181db",
gasUsed: "0x48",
input: "0x557904b74478f8810cc02198544a030d1829bb491e14fe1dd0354e933c5e87bd",
output: "0x5fb393023b12544491a5b8fb057943b4ebf5b1401e88e44a7800000000000000",
to: "0x0000000000000000000000000000000000000002",
type: "STATICCALL"
}],
from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0",
gas: "0x1a310",
gasUsed: "0xfcb6",
input: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000",
to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d",
type: "CALL",
value: "0x0"
}
```
Things to note about the call tracer:
- Calls to precompiles are also included in the result
- In case a frame reverts, the field `output` will contain the raw return data
- In case the top level frame reverts, its `revertReason` field will contain the parsed reason of revert as returned by the Solidity contract
#### Config
`callTracer` accepts two options:
- `onlyTopCall: true` instructs the tracer to only process the main (top-level) call and none of the sub-calls. This avoids extra processing for each call frame if only the top-level call info are required.
- `withLog: true` instructs the tracer to also collect the logs emitted during each call.
Example invokation with the `onlyTopCall` flag:
```terminal
> debug.traceTransaction('0xc73e70f6d60e63a71dabf90b9983f2cdd56b0cb7bcf1a205f638d630a95bba73', { tracer: 'callTracer', tracerConfig: { onlyTopCall: true } })
```
### prestateTracer
The prestate tracer has two modes: `prestate` and `diff`. The `prestate` mode returns
the accounts necessary to execute a given transaction. `diff` mode returns the differences
between the transaction's pre and post-state (i.e. what changed because the transaction
happened). The `prestateTracer` defaults to `prestate` mode. It reexecutes the given
transaction and tracks every part of state that is touched. This is similar to the
concept of a [stateless witness](https://ethresear.ch/t/the-stateless-client-concept/172),
the difference being this tracer doesn't return any cryptographic proof, rather only the
trie leaves. The result is an object. The keys are addresses of accounts. The value is an
object with the following fields:
| field | type | description |
| ------- | ----------------- | ----------------------------- |
| balance | string | balance in Wei |
| nonce | uint64 | nonce |
| code | string | hex-encoded bytecode |
| storage | map[string]string | storage slots of the contract |
To run this tracer in `diff` mode, pass `tracerConfig: {diffMode: true}` in the APi call.
Example:
```js
debug.traceCall({from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0", to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d", data: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000"}, 'latest', {tracer: 'prestateTracer'})
```
Return:
```terminal
{
0x0000000000000000000000000000000000000002: {
balance: "0x0"
},
0x008b3b2f992c0e14edaa6e2c662bec549caa8df1: {
balance: "0x2638035a26d133809"
},
0x35a9f94af726f07b5162df7e828cc9dc8439e7d0: {
balance: "0x7a48734599f7284",
nonce: 1133
},
0xc8ba32cab1757528daf49033e3673fae77dcf05d: {
balance: "0x0",
code: "0x608060405234801561001057600080fd5b50600436106100885760003560e01c8063a9c2d...
nonce: 1,
storage: {
0x0000000000000000000000000000000000000000000000000000000000000000: "0x000000000000000000000000000000000000000000000000000000000024aea6",
0x59fb7853eb21f604d010b94c123acbeae621f09ce15ee5d7616485b1e78a72e9: "0x00000000000000c42b56a52aedf18667c8ae258a0280a8912641c80c48cd9548",
0x8d8ebb65ec00cb973d4fe086a607728fd1b9de14aa48208381eed9592f0dee9a: "0x00000000000000784ae4881e40b1f5ebb4437905fbb8a5914454123b0293b35f",
0xff896b09014882056009dedb136458f017fcef9a4729467d0d00b4fd413fb1f1: "0x000000000000000e78ac39cb1c20e9edc753623b153705d0ccc487e31f9d6749"
}
}
}
```
Return (same call with `{diffMode: True}`):
```terminal
{
post: {
0x35a9f94af726f07b5162df7e828cc9dc8439e7d0: {
nonce: 1135
}
},
pre: {
0x35a9f94af726f07b5162df7e828cc9dc8439e7d0: {
balance: "0x7a48429e177130a",
nonce: 1134
}
}
}
```
### noopTracer
This tracer is noop. It returns an empty object and is only meant for testing the setup.
## Javascript tracers
There are also a set of tracers written in Javascript. These are less performant than
the Go native tracers because of overheads associated with interpreting the Javascript
in Geth's Go environment.
### bigram
`bigramTracer` counts the opcode bigrams, i.e. how many times 2 opcodes were executed
one after the other.
Example:
```js
debug.traceCall({from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0", to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d", data: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000"}, 'latest', {tracer: 'bigramTracer'})
```
Returns:
```terminal
{
ADD-ADD: 1,
ADD-AND: 2,
ADD-CALLDATALOAD: 1,
ADD-DUP1: 2,
ADD-DUP2: 2,
ADD-GT: 1,
ADD-MLOAD: 1,
ADD-MSTORE: 4,
ADD-PUSH1: 1,
ADD-PUSH2: 4,
ADD-SLT: 1,
ADD-SWAP1: 10,
ADD-SWAP2: 1,
ADD-SWAP3: 1,
ADD-SWAP4: 3,
ADD-SWAP5: 1,
AND-DUP3: 2,
AND-ISZERO: 4,
...
}
```
### evmdis
`evmdisTracer` returns sufficient information from a trace to perform
[evmdis](https://github.com/Arachnid/evmdis)-style disassembly
Example:
```js
> debug.traceCall({from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0", to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d", data: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000"}, 'latest', {tracer: 'evmdisTracer'})
```
Returns:
```terminal
[{
depth: 1,
len: 2,
op: 96,
result: ["80"]
}, {
depth: 1,
len: 2,
op: 96,
result: ["40"]
}, {
depth: 1,
op: 82,
result: []
}, {
depth: 1,
op: 52,
result: ["0"]
}, {
depth: 1,
op: 128,
result: ["0", "0"]
}, {
depth: 1,
op: 21,
result: ["1"]
}, {
depth: 1,
len: 3,
op: 97,
result: ["10"]
}, {
depth: 1,
op: 87,
result: []
}, {
depth: 1,
op: 91,
pc: 16,
result: []
},
...
```
### opcount
`opcountTracer` counts the total number of opcodes executed and simply
returns the number.
Example:
```js
debug.traceCall({from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0", to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d", data: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000"}, 'latest', {tracer: 'opcountTracer'})
```
Returns:
```terminal
1384
```
### trigram
`trigramTracer` counts the opcode trigrams. Trigrams are the possible combinations
of three opcodes this tracer reports how many times each combination is seen during
execution.
Example:
```js
debug.traceCall({from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0", to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d", data: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000"}, 'latest', {tracer: 'trigramTracer'})
```
Returns:
```terminal
{
--PUSH1: 1,
-PUSH1-MSTORE: 1,
ADD-ADD-GT: 1,
ADD-AND-DUP3: 2,
ADD-CALLDATALOAD-PUSH8: 1,
ADD-DUP1-PUSH1: 2,
ADD-DUP2-ADD: 1,
ADD-DUP2-MSTORE: 1,
ADD-GT-ISZERO: 1,
ADD-MLOAD-DUP6: 1,
ADD-MSTORE-ADD: 1,
ADD-MSTORE-PUSH1: 2,
ADD-MSTORE-PUSH32: 1,
ADD-PUSH1-KECCAK256: 1,
ADD-PUSH2-JUMP: 2,
ADD-PUSH2-JUMPI: 1,
ADD-PUSH2-SWAP2: 1,
ADD-SLT-PUSH2: 1,
...
}
```
### unigram
`unigramTracer` counts the frequency of occurrance of each opcode.
Example:
```js
> debug.traceCall({from: "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0", to: "0xc8ba32cab1757528daf49033e3673fae77dcf05d", data: "0xd1a2eab2000000000000000000000000000000000000000000000000000000000024aea100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000204895cd480cc8412691a880028a25aec86786f1ed2aa5562bc400000000000000c6403c14f35be1da6f433eadbb6e9178a47fbc7c6c1d568d2f2b876e929089c8d8db646304fd001a187dc8a600000000000000000000000000000000"}, 'latest', {tracer: 'unigramTracer'})
```
Returns:
```terminal
{
ADD: 36,
AND: 23,
BYTE: 4,
CALLDATACOPY: 1,
CALLDATALOAD: 6,
CALLDATASIZE: 2,
CALLVALUE: 1,
DIV: 9,
DUP1: 29,
DUP10: 2,
DUP11: 1,
DUP12: 3,
DUP13: 2,
...
}
```
## State overrides
It is possible to give temporary state modifications to Geth in order to simulate
the effects of `eth_call`. For example, some new byetcode could be deployed to
some address *temporarily just for the duration of the execution* and then a
transaction interacting with that address canm be traced. This can be used for
scenario testing or determining the outcome of some hypothetical transaction
before executing for real.
To do this, the tracer is written as normal, but the parameter `stateOverrides` is
passed an address and some bytecode.
```js
var code = //contract bytecode
var tracer = //tracer name
debug.traceCall({from: , to: , input: }, 'latest', {stateOverrides: {'0x...': {code: code}}, tracer: tracer})
```
## Summary
This page showed how to use the tracers that come bundled with Geth. There are a set
written in Go and a set written in Javascript. They are invoked by passing their names
when calling an API method. State overrides can be used in combination with tracers to
examine precisely what the EVM will do in some hypothetical scenario.