diff --git a/cmd/evm/internal/t8ntool/opcode_counter.go b/cmd/evm/internal/t8ntool/opcode_counter.go deleted file mode 100644 index 6f0bd261bf..0000000000 --- a/cmd/evm/internal/t8ntool/opcode_counter.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2025 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package t8ntool - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" -) - -// opcodeCounter is a simple tracer that counts how many times each opcode is executed. -type opcodeCounter struct { - counts map[vm.OpCode]uint64 -} - -func newOpcodeCounter() *opcodeCounter { - return &opcodeCounter{ - counts: make(map[vm.OpCode]uint64), - } -} - -func (c *opcodeCounter) hooks() *tracing.Hooks { - return &tracing.Hooks{ - OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { - c.counts[vm.OpCode(op)]++ - }, - } -} - -// results returns the opcode counts keyed by opcode name. -func (c *opcodeCounter) results() map[string]uint64 { - out := make(map[string]uint64, len(c.counts)) - for op, count := range c.counts { - out[op.String()] = count - } - return out -} - -// composeHooks merges two sets of hooks into one. Both sets of hooks are called -// for each event. -func composeHooks(a, b *tracing.Hooks) *tracing.Hooks { - return &tracing.Hooks{ - OnTxStart: func(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { - if a.OnTxStart != nil { - a.OnTxStart(vm, tx, from) - } - if b.OnTxStart != nil { - b.OnTxStart(vm, tx, from) - } - }, - OnTxEnd: func(receipt *types.Receipt, err error) { - if a.OnTxEnd != nil { - a.OnTxEnd(receipt, err) - } - if b.OnTxEnd != nil { - b.OnTxEnd(receipt, err) - } - }, - OnEnter: func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { - if a.OnEnter != nil { - a.OnEnter(depth, typ, from, to, input, gas, value) - } - if b.OnEnter != nil { - b.OnEnter(depth, typ, from, to, input, gas, value) - } - }, - OnExit: func(depth int, output []byte, gasUsed uint64, err error, reverted bool) { - if a.OnExit != nil { - a.OnExit(depth, output, gasUsed, err, reverted) - } - if b.OnExit != nil { - b.OnExit(depth, output, gasUsed, err, reverted) - } - }, - OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { - if a.OnOpcode != nil { - a.OnOpcode(pc, op, gas, cost, scope, rData, depth, err) - } - if b.OnOpcode != nil { - b.OnOpcode(pc, op, gas, cost, scope, rData, depth, err) - } - }, - OnFault: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) { - if a.OnFault != nil { - a.OnFault(pc, op, gas, cost, scope, depth, err) - } - if b.OnFault != nil { - b.OnFault(pc, op, gas, cost, scope, depth, err) - } - }, - OnSystemCallStart: func() { - if a.OnSystemCallStart != nil { - a.OnSystemCallStart() - } - if b.OnSystemCallStart != nil { - b.OnSystemCallStart() - } - }, - OnSystemCallEnd: func() { - if a.OnSystemCallEnd != nil { - a.OnSystemCallEnd() - } - if b.OnSystemCallEnd != nil { - b.OnSystemCallEnd() - } - }, - } -} diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 13fd258ab8..2e10812bf9 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers/logger" + "github.com/ethereum/go-ethereum/eth/tracers/native" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/tests" @@ -192,13 +193,17 @@ func Transition(ctx *cli.Context) error { } } // Configure opcode counter - var counter *opcodeCounter + var counter *native.OpcodeCounter if ctx.IsSet(OpcodeCountFlag.Name) && ctx.String(OpcodeCountFlag.Name) != "" { - counter = newOpcodeCounter() + counter = native.NewOpcodeCounter() if vmConfig.Tracer != nil { - vmConfig.Tracer = composeHooks(vmConfig.Tracer, counter.hooks()) + // If we have an existing tracer, multiplex with the opcode tracer + tExisting := tracers.Tracer{Hooks: vmConfig.Tracer} + tCounter := tracers.Tracer{Hooks: counter.Hooks()} + muxTracer, _ := native.NewMuxTracer([]string{"existing", "opcode"}, []*tracers.Tracer{&tExisting, &tCounter}) + vmConfig.Tracer = muxTracer.Hooks } else { - vmConfig.Tracer = counter.hooks() + vmConfig.Tracer = counter.Hooks() } } // Run the test and aggregate the result @@ -209,7 +214,7 @@ func Transition(ctx *cli.Context) error { // Write opcode counts if enabled if counter != nil { fname := ctx.String(OpcodeCountFlag.Name) - if err := saveFile(baseDir, fname, counter.results()); err != nil { + if err := saveFile(baseDir, fname, counter.Results()); err != nil { return err } } diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 37fc64f3f5..be4e5b8cf6 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -28,7 +28,7 @@ import ( ) func init() { - tracers.DefaultDirectory.Register("muxTracer", newMuxTracer, false) + tracers.DefaultDirectory.Register("muxTracer", newMuxTracerFromConfig, false) } // muxTracer is a go implementation of the Tracer interface which @@ -38,8 +38,8 @@ type muxTracer struct { tracers []*tracers.Tracer } -// newMuxTracer returns a new mux tracer. -func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) { +// newMuxTracerFromConfig returns a new mux tracer. +func newMuxTracerFromConfig(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) { var config map[string]json.RawMessage if err := json.Unmarshal(cfg, &config); err != nil { return nil, err @@ -54,7 +54,10 @@ func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params objects = append(objects, t) names = append(names, k) } + return NewMuxTracer(names, objects) +} +func NewMuxTracer(names []string, objects []*tracers.Tracer) (*tracers.Tracer, error) { t := &muxTracer{names: names, tracers: objects} return &tracers.Tracer{ Hooks: &tracing.Hooks{ diff --git a/eth/tracers/native/opcode_counter.go b/eth/tracers/native/opcode_counter.go new file mode 100644 index 0000000000..9922f9a687 --- /dev/null +++ b/eth/tracers/native/opcode_counter.go @@ -0,0 +1,50 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package native + +import ( + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/vm" +) + +// OpcodeCounter is a simple tracer that counts how many times each opcode is executed. +type OpcodeCounter struct { + counts map[vm.OpCode]uint64 +} + +func NewOpcodeCounter() *OpcodeCounter { + return &OpcodeCounter{ + counts: make(map[vm.OpCode]uint64), + } +} + +func (c *OpcodeCounter) Hooks() *tracing.Hooks { + return &tracing.Hooks{ + OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + c.counts[vm.OpCode(op)]++ + }, + } +} + +// Results returns the opcode counts keyed by opcode name. +func (c *OpcodeCounter) Results() map[string]uint64 { + out := make(map[string]uint64, len(c.counts)) + for op, count := range c.counts { + out[op.String()] = count + } + return out +}