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
+}