Skip to content

Commit 31f4dc4

Browse files
s1nauprendis
authored andcommitted
core,eth: call frame tracing (ethereum#23087)
This change introduces 2 new optional methods; `enter()` and `exit()` for js tracers, and makes `step()` optiona. The two new methods are invoked when entering and exiting a call frame (but not invoked for the outermost scope, which has it's own methods). Currently these are the data fields passed to each of them: enter: type (opcode), from, to, input, gas, value exit: output, gasUsed, error The PR also comes with a re-write of the callTracer. As a backup we keep the previous tracing script under the name `callTracerLegacy`. Behaviour of both tracers are equivalent for the most part, although there are some small differences (improvements), where the new tracer is more correct / has more information.
1 parent 3db2e88 commit 31f4dc4

37 files changed

+2289
-285
lines changed

core/vm/access_list_tracer.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ func (*AccessListTracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost
166166

167167
func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}
168168

169+
func (*AccessListTracer) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
170+
}
171+
172+
func (*AccessListTracer) CaptureExit(output []byte, gasUsed uint64, err error) {}
173+
169174
// AccessList returns the current accesslist maintained by the tracer.
170175
func (a *AccessListTracer) AccessList() types.AccessList {
171176
return a.list.accessList()

core/vm/evm.go

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,19 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
203203
evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)
204204

205205
// Capture the tracer start/end events in debug mode
206-
if evm.Config.Debug && evm.depth == 0 {
207-
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
208-
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
209-
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
210-
}(gas, time.Now())
206+
if evm.Config.Debug {
207+
if evm.depth == 0 {
208+
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
209+
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
210+
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
211+
}(gas, time.Now())
212+
} else {
213+
// Handle tracer events for entering and exiting a call frame
214+
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
215+
defer func(startGas uint64) {
216+
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
217+
}(gas)
218+
}
211219
}
212220

213221
if isPrecompile {
@@ -269,6 +277,14 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
269277
}
270278
var snapshot = evm.StateDB.Snapshot()
271279

280+
// Invoke tracer hooks that signal entering/exiting a call frame
281+
if evm.Config.Debug {
282+
evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
283+
defer func(startGas uint64) {
284+
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
285+
}(gas)
286+
}
287+
272288
// It is allowed to call precompiles, even via delegatecall
273289
if p, isPrecompile := evm.precompile(addr); isPrecompile {
274290
ret, gas, err = RunPrecompiledContract(p, input, gas)
@@ -305,6 +321,14 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
305321
}
306322
var snapshot = evm.StateDB.Snapshot()
307323

324+
// Invoke tracer hooks that signal entering/exiting a call frame
325+
if evm.Config.Debug {
326+
evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
327+
defer func(startGas uint64) {
328+
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
329+
}(gas)
330+
}
331+
308332
// It is allowed to call precompiles, even via delegatecall
309333
if p, isPrecompile := evm.precompile(addr); isPrecompile {
310334
ret, gas, err = RunPrecompiledContract(p, input, gas)
@@ -350,6 +374,14 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
350374
// future scenarios
351375
evm.StateDB.AddBalance(addr, big0)
352376

377+
// Invoke tracer hooks that signal entering/exiting a call frame
378+
if evm.Config.Debug {
379+
evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
380+
defer func(startGas uint64) {
381+
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
382+
}(gas)
383+
}
384+
353385
if p, isPrecompile := evm.precompile(addr); isPrecompile {
354386
ret, gas, err = RunPrecompiledContract(p, input, gas)
355387
} else {
@@ -389,7 +421,7 @@ func (c *codeAndHash) Hash() common.Hash {
389421
}
390422

391423
// create creates a new contract using code as deployment code.
392-
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) {
424+
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
393425
// Depth check execution. Fail if we're trying to execute above the
394426
// limit.
395427
if evm.depth > int(params.CallCreateDepth) {
@@ -427,9 +459,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
427459
return nil, address, gas, nil
428460
}
429461

430-
if evm.Config.Debug && evm.depth == 0 {
431-
evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
462+
if evm.Config.Debug {
463+
if evm.depth == 0 {
464+
evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
465+
} else {
466+
evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
467+
}
432468
}
469+
433470
start := time.Now()
434471

435472
ret, err := evm.interpreter.Run(contract, nil, false)
@@ -467,16 +504,20 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
467504
}
468505
}
469506

470-
if evm.Config.Debug && evm.depth == 0 {
471-
evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
507+
if evm.Config.Debug {
508+
if evm.depth == 0 {
509+
evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
510+
} else {
511+
evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err)
512+
}
472513
}
473514
return ret, address, contract.Gas, err
474515
}
475516

476517
// Create creates a new contract using code as deployment code.
477518
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
478519
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
479-
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr)
520+
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
480521
}
481522

482523
// Create2 creates a new contract using code as deployment code.
@@ -486,7 +527,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
486527
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
487528
codeAndHash := &codeAndHash{code: code}
488529
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
489-
return evm.create(caller, codeAndHash, gas, endowment, contractAddr)
530+
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
490531
}
491532

492533
// ChainConfig returns the environment's chain configuration

core/vm/instructions.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,10 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
791791
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
792792
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
793793
interpreter.evm.StateDB.Suicide(scope.Contract.Address())
794+
if interpreter.cfg.Debug {
795+
interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
796+
interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil)
797+
}
794798
return nil, nil
795799
}
796800

core/vm/logger.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ func (s *StructLog) ErrorString() string {
106106
type Tracer interface {
107107
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
108108
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
109+
CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
110+
CaptureExit(output []byte, gasUsed uint64, err error)
109111
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
110112
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
111113
}
@@ -225,6 +227,11 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration
225227
}
226228
}
227229

230+
func (l *StructLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
231+
}
232+
233+
func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
234+
228235
// StructLogs returns the captured log entries.
229236
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
230237

@@ -342,3 +349,8 @@ func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, e
342349
fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
343350
output, gasUsed, err)
344351
}
352+
353+
func (t *mdLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
354+
}
355+
356+
func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}

core/vm/logger_json.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,8 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration,
8787
}
8888
l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg})
8989
}
90+
91+
func (l *JSONLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
92+
}
93+
94+
func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}

0 commit comments

Comments
 (0)