eth/tracers: improve tracing performance (#23016)
Improves the performance of debug.traceTransaction
This commit is contained in:
parent
bbbeb7d8ba
commit
f5f906dd0d
@ -4,11 +4,11 @@ package vm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = (*structLogMarshaling)(nil)
|
var _ = (*structLogMarshaling)(nil)
|
||||||
@ -22,8 +22,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
|
|||||||
GasCost math.HexOrDecimal64 `json:"gasCost"`
|
GasCost math.HexOrDecimal64 `json:"gasCost"`
|
||||||
Memory hexutil.Bytes `json:"memory"`
|
Memory hexutil.Bytes `json:"memory"`
|
||||||
MemorySize int `json:"memSize"`
|
MemorySize int `json:"memSize"`
|
||||||
Stack []*math.HexOrDecimal256 `json:"stack"`
|
Stack []uint256.Int `json:"stack"`
|
||||||
ReturnStack []math.HexOrDecimal64 `json:"returnStack"`
|
|
||||||
ReturnData hexutil.Bytes `json:"returnData"`
|
ReturnData hexutil.Bytes `json:"returnData"`
|
||||||
Storage map[common.Hash]common.Hash `json:"-"`
|
Storage map[common.Hash]common.Hash `json:"-"`
|
||||||
Depth int `json:"depth"`
|
Depth int `json:"depth"`
|
||||||
@ -39,12 +38,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
|
|||||||
enc.GasCost = math.HexOrDecimal64(s.GasCost)
|
enc.GasCost = math.HexOrDecimal64(s.GasCost)
|
||||||
enc.Memory = s.Memory
|
enc.Memory = s.Memory
|
||||||
enc.MemorySize = s.MemorySize
|
enc.MemorySize = s.MemorySize
|
||||||
if s.Stack != nil {
|
enc.Stack = s.Stack
|
||||||
enc.Stack = make([]*math.HexOrDecimal256, len(s.Stack))
|
|
||||||
for k, v := range s.Stack {
|
|
||||||
enc.Stack[k] = (*math.HexOrDecimal256)(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enc.ReturnData = s.ReturnData
|
enc.ReturnData = s.ReturnData
|
||||||
enc.Storage = s.Storage
|
enc.Storage = s.Storage
|
||||||
enc.Depth = s.Depth
|
enc.Depth = s.Depth
|
||||||
@ -64,7 +58,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
|
|||||||
GasCost *math.HexOrDecimal64 `json:"gasCost"`
|
GasCost *math.HexOrDecimal64 `json:"gasCost"`
|
||||||
Memory *hexutil.Bytes `json:"memory"`
|
Memory *hexutil.Bytes `json:"memory"`
|
||||||
MemorySize *int `json:"memSize"`
|
MemorySize *int `json:"memSize"`
|
||||||
Stack []*math.HexOrDecimal256 `json:"stack"`
|
Stack []uint256.Int `json:"stack"`
|
||||||
ReturnData *hexutil.Bytes `json:"returnData"`
|
ReturnData *hexutil.Bytes `json:"returnData"`
|
||||||
Storage map[common.Hash]common.Hash `json:"-"`
|
Storage map[common.Hash]common.Hash `json:"-"`
|
||||||
Depth *int `json:"depth"`
|
Depth *int `json:"depth"`
|
||||||
@ -94,10 +88,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
|
|||||||
s.MemorySize = *dec.MemorySize
|
s.MemorySize = *dec.MemorySize
|
||||||
}
|
}
|
||||||
if dec.Stack != nil {
|
if dec.Stack != nil {
|
||||||
s.Stack = make([]*big.Int, len(dec.Stack))
|
s.Stack = dec.Stack
|
||||||
for k, v := range dec.Stack {
|
|
||||||
s.Stack[k] = (*big.Int)(v)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if dec.ReturnData != nil {
|
if dec.ReturnData != nil {
|
||||||
s.ReturnData = *dec.ReturnData
|
s.ReturnData = *dec.ReturnData
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Storage represents a contract's storage.
|
// Storage represents a contract's storage.
|
||||||
@ -66,7 +67,7 @@ type StructLog struct {
|
|||||||
GasCost uint64 `json:"gasCost"`
|
GasCost uint64 `json:"gasCost"`
|
||||||
Memory []byte `json:"memory"`
|
Memory []byte `json:"memory"`
|
||||||
MemorySize int `json:"memSize"`
|
MemorySize int `json:"memSize"`
|
||||||
Stack []*big.Int `json:"stack"`
|
Stack []uint256.Int `json:"stack"`
|
||||||
ReturnData []byte `json:"returnData"`
|
ReturnData []byte `json:"returnData"`
|
||||||
Storage map[common.Hash]common.Hash `json:"-"`
|
Storage map[common.Hash]common.Hash `json:"-"`
|
||||||
Depth int `json:"depth"`
|
Depth int `json:"depth"`
|
||||||
@ -76,7 +77,6 @@ type StructLog struct {
|
|||||||
|
|
||||||
// overrides for gencodec
|
// overrides for gencodec
|
||||||
type structLogMarshaling struct {
|
type structLogMarshaling struct {
|
||||||
Stack []*math.HexOrDecimal256
|
|
||||||
Gas math.HexOrDecimal64
|
Gas math.HexOrDecimal64
|
||||||
GasCost math.HexOrDecimal64
|
GasCost math.HexOrDecimal64
|
||||||
Memory hexutil.Bytes
|
Memory hexutil.Bytes
|
||||||
@ -135,6 +135,14 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
|
|||||||
return logger
|
return logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset clears the data held by the logger.
|
||||||
|
func (l *StructLogger) Reset() {
|
||||||
|
l.storage = make(map[common.Address]Storage)
|
||||||
|
l.output = make([]byte, 0)
|
||||||
|
l.logs = l.logs[:0]
|
||||||
|
l.err = nil
|
||||||
|
}
|
||||||
|
|
||||||
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
||||||
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||||
}
|
}
|
||||||
@ -157,16 +165,16 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
|||||||
copy(mem, memory.Data())
|
copy(mem, memory.Data())
|
||||||
}
|
}
|
||||||
// Copy a snapshot of the current stack state to a new buffer
|
// Copy a snapshot of the current stack state to a new buffer
|
||||||
var stck []*big.Int
|
var stck []uint256.Int
|
||||||
if !l.cfg.DisableStack {
|
if !l.cfg.DisableStack {
|
||||||
stck = make([]*big.Int, len(stack.Data()))
|
stck = make([]uint256.Int, len(stack.Data()))
|
||||||
for i, item := range stack.Data() {
|
for i, item := range stack.Data() {
|
||||||
stck[i] = new(big.Int).Set(item.ToBig())
|
stck[i] = item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Copy a snapshot of the current storage to a new container
|
// Copy a snapshot of the current storage to a new container
|
||||||
var storage Storage
|
var storage Storage
|
||||||
if !l.cfg.DisableStorage {
|
if !l.cfg.DisableStorage && (op == SLOAD || op == SSTORE) {
|
||||||
// initialise new changed values storage container for this contract
|
// initialise new changed values storage container for this contract
|
||||||
// if not present.
|
// if not present.
|
||||||
if l.storage[contract.Address()] == nil {
|
if l.storage[contract.Address()] == nil {
|
||||||
@ -179,16 +187,16 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
|||||||
value = env.StateDB.GetState(contract.Address(), address)
|
value = env.StateDB.GetState(contract.Address(), address)
|
||||||
)
|
)
|
||||||
l.storage[contract.Address()][address] = value
|
l.storage[contract.Address()][address] = value
|
||||||
}
|
storage = l.storage[contract.Address()].Copy()
|
||||||
// capture SSTORE opcodes and record the written entry in the local storage.
|
} else if op == SSTORE && stack.len() >= 2 {
|
||||||
if op == SSTORE && stack.len() >= 2 {
|
// capture SSTORE opcodes and record the written entry in the local storage.
|
||||||
var (
|
var (
|
||||||
value = common.Hash(stack.data[stack.len()-2].Bytes32())
|
value = common.Hash(stack.data[stack.len()-2].Bytes32())
|
||||||
address = common.Hash(stack.data[stack.len()-1].Bytes32())
|
address = common.Hash(stack.data[stack.len()-1].Bytes32())
|
||||||
)
|
)
|
||||||
l.storage[contract.Address()][address] = value
|
l.storage[contract.Address()][address] = value
|
||||||
|
storage = l.storage[contract.Address()].Copy()
|
||||||
}
|
}
|
||||||
storage = l.storage[contract.Address()].Copy()
|
|
||||||
}
|
}
|
||||||
var rdata []byte
|
var rdata []byte
|
||||||
if !l.cfg.DisableReturnData {
|
if !l.cfg.DisableReturnData {
|
||||||
@ -238,7 +246,7 @@ func WriteTrace(writer io.Writer, logs []StructLog) {
|
|||||||
if len(log.Stack) > 0 {
|
if len(log.Stack) > 0 {
|
||||||
fmt.Fprintln(writer, "Stack:")
|
fmt.Fprintln(writer, "Stack:")
|
||||||
for i := len(log.Stack) - 1; i >= 0; i-- {
|
for i := len(log.Stack) - 1; i >= 0; i-- {
|
||||||
fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
|
fmt.Fprintf(writer, "%08d %s\n", len(log.Stack)-i-1, log.Stack[i].Hex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(log.Memory) > 0 {
|
if len(log.Memory) > 0 {
|
||||||
@ -314,7 +322,7 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64
|
|||||||
// format stack
|
// format stack
|
||||||
var a []string
|
var a []string
|
||||||
for _, elem := range stack.data {
|
for _, elem := range stack.data {
|
||||||
a = append(a, fmt.Sprintf("%v", elem.String()))
|
a = append(a, elem.Hex())
|
||||||
}
|
}
|
||||||
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
|
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
|
||||||
fmt.Fprintf(t.out, "%10v |", b)
|
fmt.Fprintf(t.out, "%10v |", b)
|
||||||
|
@ -57,7 +57,6 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
|
|||||||
Gas: gas,
|
Gas: gas,
|
||||||
GasCost: cost,
|
GasCost: cost,
|
||||||
MemorySize: memory.Len(),
|
MemorySize: memory.Len(),
|
||||||
Storage: nil,
|
|
||||||
Depth: depth,
|
Depth: depth,
|
||||||
RefundCounter: env.StateDB.GetRefund(),
|
RefundCounter: env.StateDB.GetRefund(),
|
||||||
Err: err,
|
Err: err,
|
||||||
@ -66,12 +65,7 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
|
|||||||
log.Memory = memory.Data()
|
log.Memory = memory.Data()
|
||||||
}
|
}
|
||||||
if !l.cfg.DisableStack {
|
if !l.cfg.DisableStack {
|
||||||
//TODO(@holiman) improve this
|
log.Stack = stack.data
|
||||||
logstack := make([]*big.Int, len(stack.Data()))
|
|
||||||
for i, item := range stack.Data() {
|
|
||||||
logstack[i] = item.ToBig()
|
|
||||||
}
|
|
||||||
log.Stack = logstack
|
|
||||||
}
|
}
|
||||||
if !l.cfg.DisableReturnData {
|
if !l.cfg.DisableReturnData {
|
||||||
log.ReturnData = rData
|
log.ReturnData = rData
|
||||||
|
@ -300,3 +300,81 @@ func jsonEqual(x, y interface{}) bool {
|
|||||||
}
|
}
|
||||||
return reflect.DeepEqual(xTrace, yTrace)
|
return reflect.DeepEqual(xTrace, yTrace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkTransactionTrace(b *testing.B) {
|
||||||
|
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
from := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
gas := uint64(1000000) // 1M gas
|
||||||
|
to := common.HexToAddress("0x00000000000000000000000000000000deadbeef")
|
||||||
|
signer := types.LatestSignerForChainID(big.NewInt(1337))
|
||||||
|
tx, err := types.SignNewTx(key, signer,
|
||||||
|
&types.LegacyTx{
|
||||||
|
Nonce: 1,
|
||||||
|
GasPrice: big.NewInt(500),
|
||||||
|
Gas: gas,
|
||||||
|
To: &to,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
txContext := vm.TxContext{
|
||||||
|
Origin: from,
|
||||||
|
GasPrice: tx.GasPrice(),
|
||||||
|
}
|
||||||
|
context := vm.BlockContext{
|
||||||
|
CanTransfer: core.CanTransfer,
|
||||||
|
Transfer: core.Transfer,
|
||||||
|
Coinbase: common.Address{},
|
||||||
|
BlockNumber: new(big.Int).SetUint64(uint64(5)),
|
||||||
|
Time: new(big.Int).SetUint64(uint64(5)),
|
||||||
|
Difficulty: big.NewInt(0xffffffff),
|
||||||
|
GasLimit: gas,
|
||||||
|
}
|
||||||
|
alloc := core.GenesisAlloc{}
|
||||||
|
// The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns
|
||||||
|
// the address
|
||||||
|
loop := []byte{
|
||||||
|
byte(vm.JUMPDEST), // [ count ]
|
||||||
|
byte(vm.PUSH1), 0, // jumpdestination
|
||||||
|
byte(vm.JUMP),
|
||||||
|
}
|
||||||
|
alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = core.GenesisAccount{
|
||||||
|
Nonce: 1,
|
||||||
|
Code: loop,
|
||||||
|
Balance: big.NewInt(1),
|
||||||
|
}
|
||||||
|
alloc[from] = core.GenesisAccount{
|
||||||
|
Nonce: 1,
|
||||||
|
Code: []byte{},
|
||||||
|
Balance: big.NewInt(500000000000000),
|
||||||
|
}
|
||||||
|
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false)
|
||||||
|
// Create the tracer, the EVM environment and run it
|
||||||
|
tracer := vm.NewStructLogger(&vm.LogConfig{
|
||||||
|
Debug: false,
|
||||||
|
//DisableStorage: true,
|
||||||
|
//DisableMemory: true,
|
||||||
|
//DisableReturnData: true,
|
||||||
|
})
|
||||||
|
evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer})
|
||||||
|
msg, err := tx.AsMessage(signer, nil)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
snap := statedb.Snapshot()
|
||||||
|
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||||
|
_, err = st.TransitionDb()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
statedb.RevertToSnapshot(snap)
|
||||||
|
if have, want := len(tracer.StructLogs()), 244752; have != want {
|
||||||
|
b.Fatalf("trace wrong, want %d steps, have %d", want, have)
|
||||||
|
}
|
||||||
|
tracer.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1117,7 +1117,7 @@ func FormatLogs(logs []vm.StructLog) []StructLogRes {
|
|||||||
if trace.Stack != nil {
|
if trace.Stack != nil {
|
||||||
stack := make([]string, len(trace.Stack))
|
stack := make([]string, len(trace.Stack))
|
||||||
for i, stackValue := range trace.Stack {
|
for i, stackValue := range trace.Stack {
|
||||||
stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32))
|
stack[i] = stackValue.Hex()
|
||||||
}
|
}
|
||||||
formatted[index].Stack = &stack
|
formatted[index].Stack = &stack
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user