Skip to content

Commit f5f906d

Browse files
authored
eth/tracers: improve tracing performance (#23016)
Improves the performance of debug.traceTransaction
1 parent bbbeb7d commit f5f906d

File tree

5 files changed

+105
-34
lines changed

5 files changed

+105
-34
lines changed

core/vm/gen_structlog.go

Lines changed: 5 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/vm/logger.go

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/ethereum/go-ethereum/common/math"
3030
"github.com/ethereum/go-ethereum/core/types"
3131
"github.com/ethereum/go-ethereum/params"
32+
"github.com/holiman/uint256"
3233
)
3334

3435
// Storage represents a contract's storage.
@@ -66,7 +67,7 @@ type StructLog struct {
6667
GasCost uint64 `json:"gasCost"`
6768
Memory []byte `json:"memory"`
6869
MemorySize int `json:"memSize"`
69-
Stack []*big.Int `json:"stack"`
70+
Stack []uint256.Int `json:"stack"`
7071
ReturnData []byte `json:"returnData"`
7172
Storage map[common.Hash]common.Hash `json:"-"`
7273
Depth int `json:"depth"`
@@ -76,7 +77,6 @@ type StructLog struct {
7677

7778
// overrides for gencodec
7879
type structLogMarshaling struct {
79-
Stack []*math.HexOrDecimal256
8080
Gas math.HexOrDecimal64
8181
GasCost math.HexOrDecimal64
8282
Memory hexutil.Bytes
@@ -135,6 +135,14 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
135135
return logger
136136
}
137137

138+
// Reset clears the data held by the logger.
139+
func (l *StructLogger) Reset() {
140+
l.storage = make(map[common.Address]Storage)
141+
l.output = make([]byte, 0)
142+
l.logs = l.logs[:0]
143+
l.err = nil
144+
}
145+
138146
// CaptureStart implements the Tracer interface to initialize the tracing operation.
139147
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
140148
}
@@ -157,16 +165,16 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
157165
copy(mem, memory.Data())
158166
}
159167
// Copy a snapshot of the current stack state to a new buffer
160-
var stck []*big.Int
168+
var stck []uint256.Int
161169
if !l.cfg.DisableStack {
162-
stck = make([]*big.Int, len(stack.Data()))
170+
stck = make([]uint256.Int, len(stack.Data()))
163171
for i, item := range stack.Data() {
164-
stck[i] = new(big.Int).Set(item.ToBig())
172+
stck[i] = item
165173
}
166174
}
167175
// Copy a snapshot of the current storage to a new container
168176
var storage Storage
169-
if !l.cfg.DisableStorage {
177+
if !l.cfg.DisableStorage && (op == SLOAD || op == SSTORE) {
170178
// initialise new changed values storage container for this contract
171179
// if not present.
172180
if l.storage[contract.Address()] == nil {
@@ -179,16 +187,16 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
179187
value = env.StateDB.GetState(contract.Address(), address)
180188
)
181189
l.storage[contract.Address()][address] = value
182-
}
183-
// capture SSTORE opcodes and record the written entry in the local storage.
184-
if op == SSTORE && stack.len() >= 2 {
190+
storage = l.storage[contract.Address()].Copy()
191+
} else if op == SSTORE && stack.len() >= 2 {
192+
// capture SSTORE opcodes and record the written entry in the local storage.
185193
var (
186194
value = common.Hash(stack.data[stack.len()-2].Bytes32())
187195
address = common.Hash(stack.data[stack.len()-1].Bytes32())
188196
)
189197
l.storage[contract.Address()][address] = value
198+
storage = l.storage[contract.Address()].Copy()
190199
}
191-
storage = l.storage[contract.Address()].Copy()
192200
}
193201
var rdata []byte
194202
if !l.cfg.DisableReturnData {
@@ -238,7 +246,7 @@ func WriteTrace(writer io.Writer, logs []StructLog) {
238246
if len(log.Stack) > 0 {
239247
fmt.Fprintln(writer, "Stack:")
240248
for i := len(log.Stack) - 1; i >= 0; i-- {
241-
fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
249+
fmt.Fprintf(writer, "%08d %s\n", len(log.Stack)-i-1, log.Stack[i].Hex())
242250
}
243251
}
244252
if len(log.Memory) > 0 {
@@ -314,7 +322,7 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64
314322
// format stack
315323
var a []string
316324
for _, elem := range stack.data {
317-
a = append(a, fmt.Sprintf("%v", elem.String()))
325+
a = append(a, elem.Hex())
318326
}
319327
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
320328
fmt.Fprintf(t.out, "%10v |", b)

core/vm/logger_json.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
5757
Gas: gas,
5858
GasCost: cost,
5959
MemorySize: memory.Len(),
60-
Storage: nil,
6160
Depth: depth,
6261
RefundCounter: env.StateDB.GetRefund(),
6362
Err: err,
@@ -66,12 +65,7 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
6665
log.Memory = memory.Data()
6766
}
6867
if !l.cfg.DisableStack {
69-
//TODO(@holiman) improve this
70-
logstack := make([]*big.Int, len(stack.Data()))
71-
for i, item := range stack.Data() {
72-
logstack[i] = item.ToBig()
73-
}
74-
log.Stack = logstack
68+
log.Stack = stack.data
7569
}
7670
if !l.cfg.DisableReturnData {
7771
log.ReturnData = rData

eth/tracers/tracers_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,3 +300,81 @@ func jsonEqual(x, y interface{}) bool {
300300
}
301301
return reflect.DeepEqual(xTrace, yTrace)
302302
}
303+
304+
func BenchmarkTransactionTrace(b *testing.B) {
305+
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
306+
from := crypto.PubkeyToAddress(key.PublicKey)
307+
gas := uint64(1000000) // 1M gas
308+
to := common.HexToAddress("0x00000000000000000000000000000000deadbeef")
309+
signer := types.LatestSignerForChainID(big.NewInt(1337))
310+
tx, err := types.SignNewTx(key, signer,
311+
&types.LegacyTx{
312+
Nonce: 1,
313+
GasPrice: big.NewInt(500),
314+
Gas: gas,
315+
To: &to,
316+
})
317+
if err != nil {
318+
b.Fatal(err)
319+
}
320+
txContext := vm.TxContext{
321+
Origin: from,
322+
GasPrice: tx.GasPrice(),
323+
}
324+
context := vm.BlockContext{
325+
CanTransfer: core.CanTransfer,
326+
Transfer: core.Transfer,
327+
Coinbase: common.Address{},
328+
BlockNumber: new(big.Int).SetUint64(uint64(5)),
329+
Time: new(big.Int).SetUint64(uint64(5)),
330+
Difficulty: big.NewInt(0xffffffff),
331+
GasLimit: gas,
332+
}
333+
alloc := core.GenesisAlloc{}
334+
// The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns
335+
// the address
336+
loop := []byte{
337+
byte(vm.JUMPDEST), // [ count ]
338+
byte(vm.PUSH1), 0, // jumpdestination
339+
byte(vm.JUMP),
340+
}
341+
alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = core.GenesisAccount{
342+
Nonce: 1,
343+
Code: loop,
344+
Balance: big.NewInt(1),
345+
}
346+
alloc[from] = core.GenesisAccount{
347+
Nonce: 1,
348+
Code: []byte{},
349+
Balance: big.NewInt(500000000000000),
350+
}
351+
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false)
352+
// Create the tracer, the EVM environment and run it
353+
tracer := vm.NewStructLogger(&vm.LogConfig{
354+
Debug: false,
355+
//DisableStorage: true,
356+
//DisableMemory: true,
357+
//DisableReturnData: true,
358+
})
359+
evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer})
360+
msg, err := tx.AsMessage(signer, nil)
361+
if err != nil {
362+
b.Fatalf("failed to prepare transaction for tracing: %v", err)
363+
}
364+
b.ResetTimer()
365+
b.ReportAllocs()
366+
367+
for i := 0; i < b.N; i++ {
368+
snap := statedb.Snapshot()
369+
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
370+
_, err = st.TransitionDb()
371+
if err != nil {
372+
b.Fatal(err)
373+
}
374+
statedb.RevertToSnapshot(snap)
375+
if have, want := len(tracer.StructLogs()), 244752; have != want {
376+
b.Fatalf("trace wrong, want %d steps, have %d", want, have)
377+
}
378+
tracer.Reset()
379+
}
380+
}

internal/ethapi/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1117,7 +1117,7 @@ func FormatLogs(logs []vm.StructLog) []StructLogRes {
11171117
if trace.Stack != nil {
11181118
stack := make([]string, len(trace.Stack))
11191119
for i, stackValue := range trace.Stack {
1120-
stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32))
1120+
stack[i] = stackValue.Hex()
11211121
}
11221122
formatted[index].Stack = &stack
11231123
}

0 commit comments

Comments
 (0)