From 5258785c81959109138ebeca613f12c277188abc Mon Sep 17 00:00:00 2001 From: Péter Szilágyi Date: Thu, 21 Dec 2017 13:56:11 +0200 Subject: cmd, core, eth/tracers: support fancier js tracing (#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 --- core/vm/evm.go | 27 ++++++++++++++++++++++++--- core/vm/interpreter.go | 17 +++++++++++------ core/vm/logger.go | 10 ++++++++++ 3 files changed, 45 insertions(+), 9 deletions(-) (limited to 'core/vm') diff --git a/core/vm/evm.go b/core/vm/evm.go index cbb5a03ce..a3f3a97cb 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -19,6 +19,7 @@ package vm import ( "math/big" "sync/atomic" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -165,13 +166,23 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) - // initialise a new contract and set the code that is to be used by the - // E The contract is a scoped environment for this execution context - // only. + // Initialise a new contract and set the code that is to be used by the EVM. + // The contract is a scoped environment for this execution context only. contract := NewContract(caller, to, value, gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + start := time.Now() + + // Capture the tracer start/end events in debug mode + if evm.vmConfig.Debug && evm.depth == 0 { + evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) + + defer func() { // Lazy evaluation of the parameters + evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) + }() + } ret, err = run(evm, contract, input) + // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // 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 if evm.vmConfig.NoRecursion && evm.depth > 0 { return nil, contractAddr, gas, nil } + + if evm.vmConfig.Debug && evm.depth == 0 { + evm.vmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value) + } + start := time.Now() + ret, err = run(evm, contract, nil) + // check whether the max code size has been exceeded maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize // 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 if maxCodeSizeExceeded && err == nil { err = errMaxCodeSizeExceeded } + if evm.vmConfig.Debug && evm.depth == 0 { + evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) + } return ret, contractAddr, contract.Gas, err } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 455f970dd..482e67a3a 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -144,12 +144,17 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er ) contract.Input = input - defer func() { - if err != nil && !logged && in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) - } - }() - + if in.cfg.Debug { + defer func() { + if err != nil { + if !logged { + in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) + } else { + in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) + } + } + }() + } // The Interpreter main run loop (contextual). This loop runs until either an // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during // the execution of one of the operations or until the done flag is set by the diff --git a/core/vm/logger.go b/core/vm/logger.go index 75309da92..1a6e43ee3 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -84,7 +84,9 @@ func (s *StructLog) OpName() string { // Note that reference types are actual VM data structures; make copies // if you need to retain them beyond the current call. type Tracer interface { + CaptureStart(from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error + CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error } @@ -111,6 +113,10 @@ func NewStructLogger(cfg *LogConfig) *StructLogger { return logger } +func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { + return nil +} + // CaptureState logs a new structured log message and pushes it out to the environment // // 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 return nil } +func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { + return nil +} + func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { fmt.Printf("0x%x", output) if err != nil { -- cgit