Skip to content

Commit 5258785

Browse files
authored
cmd, core, eth/tracers: support fancier js tracing (ethereum#15516)
* cmd, core, eth/tracers: support fancier js tracing * eth, internal/web3ext: rework trace API, concurrency, chain tracing * eth/tracers: add three more JavaScript tracers * eth/tracers, vendor: swap ottovm to duktape for tracing * core, eth, internal: finalize call tracer and needed extras * eth, tests: prestate tracer, call test suite, rewinding * vendor: fix windows builds for tracer js engine * vendor: temporary duktape fix * eth/tracers: fix up 4byte and evmdis tracer * vendor: pull in latest duktape with my upstream fixes * eth: fix some review comments * eth: rename rewind to reexec to make it more obvious * core/vm: terminate tracing using defers
1 parent 1a54257 commit 5258785

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+109115
-685
lines changed

cmd/evm/json_logger.go

+10
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package main
1919
import (
2020
"encoding/json"
2121
"io"
22+
"math/big"
2223
"time"
2324

2425
"github.com/ethereum/go-ethereum/common"
@@ -35,6 +36,10 @@ func NewJSONLogger(cfg *vm.LogConfig, writer io.Writer) *JSONLogger {
3536
return &JSONLogger{json.NewEncoder(writer), cfg}
3637
}
3738

39+
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
40+
return nil
41+
}
42+
3843
// CaptureState outputs state information on the logger.
3944
func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
4045
log := vm.StructLog{
@@ -56,6 +61,11 @@ func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos
5661
return l.encoder.Encode(log)
5762
}
5863

64+
// CaptureFault outputs state information on the logger.
65+
func (l *JSONLogger) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
66+
return nil
67+
}
68+
5969
// CaptureEnd is triggered at end of execution.
6070
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
6171
type endLog struct {

core/vm/evm.go

+24-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package vm
1919
import (
2020
"math/big"
2121
"sync/atomic"
22+
"time"
2223

2324
"github.com/ethereum/go-ethereum/common"
2425
"github.com/ethereum/go-ethereum/crypto"
@@ -165,13 +166,23 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
165166
}
166167
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
167168

168-
// initialise a new contract and set the code that is to be used by the
169-
// E The contract is a scoped environment for this execution context
170-
// only.
169+
// Initialise a new contract and set the code that is to be used by the EVM.
170+
// The contract is a scoped environment for this execution context only.
171171
contract := NewContract(caller, to, value, gas)
172172
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
173173

174+
start := time.Now()
175+
176+
// Capture the tracer start/end events in debug mode
177+
if evm.vmConfig.Debug && evm.depth == 0 {
178+
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
179+
180+
defer func() { // Lazy evaluation of the parameters
181+
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
182+
}()
183+
}
174184
ret, err = run(evm, contract, input)
185+
175186
// When an error was returned by the EVM or when setting the creation code
176187
// above we revert to the snapshot and consume any gas remaining. Additionally
177188
// when we're in homestead this also counts for code storage gas errors.
@@ -338,7 +349,14 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
338349
if evm.vmConfig.NoRecursion && evm.depth > 0 {
339350
return nil, contractAddr, gas, nil
340351
}
352+
353+
if evm.vmConfig.Debug && evm.depth == 0 {
354+
evm.vmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value)
355+
}
356+
start := time.Now()
357+
341358
ret, err = run(evm, contract, nil)
359+
342360
// check whether the max code size has been exceeded
343361
maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
344362
// if the contract creation ran successfully and no errors were returned
@@ -367,6 +385,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
367385
if maxCodeSizeExceeded && err == nil {
368386
err = errMaxCodeSizeExceeded
369387
}
388+
if evm.vmConfig.Debug && evm.depth == 0 {
389+
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
390+
}
370391
return ret, contractAddr, contract.Gas, err
371392
}
372393

core/vm/interpreter.go

+11-6
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,17 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er
144144
)
145145
contract.Input = input
146146

147-
defer func() {
148-
if err != nil && !logged && in.cfg.Debug {
149-
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
150-
}
151-
}()
152-
147+
if in.cfg.Debug {
148+
defer func() {
149+
if err != nil {
150+
if !logged {
151+
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
152+
} else {
153+
in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
154+
}
155+
}
156+
}()
157+
}
153158
// The Interpreter main run loop (contextual). This loop runs until either an
154159
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
155160
// the execution of one of the operations or until the done flag is set by the

core/vm/logger.go

+10
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ func (s *StructLog) OpName() string {
8484
// Note that reference types are actual VM data structures; make copies
8585
// if you need to retain them beyond the current call.
8686
type Tracer interface {
87+
CaptureStart(from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error
8788
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
89+
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
8890
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
8991
}
9092

@@ -111,6 +113,10 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
111113
return logger
112114
}
113115

116+
func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
117+
return nil
118+
}
119+
114120
// CaptureState logs a new structured log message and pushes it out to the environment
115121
//
116122
// CaptureState also tracks SSTORE ops to track dirty values.
@@ -161,6 +167,10 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
161167
return nil
162168
}
163169

170+
func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
171+
return nil
172+
}
173+
164174
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
165175
fmt.Printf("0x%x", output)
166176
if err != nil {

0 commit comments

Comments
 (0)