mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 19:00:46 +00:00
Implement tracer interface
This commit is contained in:
parent
324dc8f588
commit
2c316a8201
3 changed files with 52 additions and 30 deletions
|
|
@ -56,27 +56,35 @@ func (l *fileWritingTracer) Write(p []byte) (n int, err error) {
|
|||
return n, nil
|
||||
}
|
||||
|
||||
// newFileWriter creates a set of hooks which wraps inner hooks (typically a logger),
|
||||
// newFileWriter creates a tracer which wraps inner hooks (typically a logger),
|
||||
// and writes the output to a file, one file per transaction.
|
||||
func newFileWriter(baseDir string, innerFn func(out io.Writer) *tracing.Hooks) *tracing.Hooks {
|
||||
func newFileWriter(baseDir string, innerFn func(out io.Writer) *tracing.Hooks) *tracers.Tracer {
|
||||
t := &fileWritingTracer{
|
||||
baseDir: baseDir,
|
||||
suffix: "jsonl",
|
||||
}
|
||||
t.inner = innerFn(t) // instantiate the inner tracer
|
||||
return t.hooks()
|
||||
return &tracers.Tracer{
|
||||
Hooks: t.hooks(),
|
||||
GetResult: func() (json.RawMessage, error) { return json.RawMessage("{}"), nil },
|
||||
Stop: func(err error) {},
|
||||
}
|
||||
}
|
||||
|
||||
// newResultWriter creates a set of hooks wraps and invokes an underlying tracer,
|
||||
// newResultWriter creates a tracer that wraps and invokes an underlying tracer,
|
||||
// and writes the result (getResult-output) to file, one per transaction.
|
||||
func newResultWriter(baseDir string, tracer *tracers.Tracer) *tracing.Hooks {
|
||||
func newResultWriter(baseDir string, tracer *tracers.Tracer) *tracers.Tracer {
|
||||
t := &fileWritingTracer{
|
||||
baseDir: baseDir,
|
||||
getResult: tracer.GetResult,
|
||||
inner: tracer.Hooks,
|
||||
suffix: "json",
|
||||
}
|
||||
return t.hooks()
|
||||
return &tracers.Tracer{
|
||||
Hooks: t.hooks(),
|
||||
GetResult: func() (json.RawMessage, error) { return json.RawMessage("{}"), nil },
|
||||
Stop: func(err error) {},
|
||||
}
|
||||
}
|
||||
|
||||
// OnTxStart creates a new output-file specific for this transaction, and invokes
|
||||
|
|
|
|||
|
|
@ -168,14 +168,15 @@ func Transition(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
// Configure tracer
|
||||
var tracer *tracers.Tracer
|
||||
if ctx.IsSet(TraceTracerFlag.Name) { // Custom tracing
|
||||
config := json.RawMessage(ctx.String(TraceTracerConfigFlag.Name))
|
||||
tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name),
|
||||
innerTracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name),
|
||||
nil, config, chainConfig)
|
||||
if err != nil {
|
||||
return NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %v", err))
|
||||
}
|
||||
vmConfig.Tracer = newResultWriter(baseDir, tracer)
|
||||
tracer = newResultWriter(baseDir, innerTracer)
|
||||
} else if ctx.Bool(TraceFlag.Name) { // JSON opcode tracing
|
||||
logConfig := &logger.Config{
|
||||
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
|
||||
|
|
@ -183,28 +184,28 @@ func Transition(ctx *cli.Context) error {
|
|||
EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
|
||||
}
|
||||
if ctx.Bool(TraceEnableCallFramesFlag.Name) {
|
||||
vmConfig.Tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
|
||||
tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
|
||||
return logger.NewJSONLoggerWithCallFrames(logConfig, out)
|
||||
})
|
||||
} else {
|
||||
vmConfig.Tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
|
||||
tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
|
||||
return logger.NewJSONLogger(logConfig, out)
|
||||
})
|
||||
}
|
||||
}
|
||||
// Configure opcode counter
|
||||
var counter *native.OpcodeCounter
|
||||
var opcodeTracer *tracers.Tracer
|
||||
if ctx.IsSet(OpcodeCountFlag.Name) && ctx.String(OpcodeCountFlag.Name) != "" {
|
||||
counter = new(native.OpcodeCounter)
|
||||
if vmConfig.Tracer != nil {
|
||||
opcodeTracer = native.NewOpcodeCounter()
|
||||
if tracer != nil {
|
||||
// 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
|
||||
mux, _ := native.NewMuxTracer([]string{"trace", "opcode"}, []*tracers.Tracer{tracer, opcodeTracer})
|
||||
vmConfig.Tracer = mux.Hooks
|
||||
} else {
|
||||
vmConfig.Tracer = counter.Hooks()
|
||||
vmConfig.Tracer = opcodeTracer.Hooks
|
||||
}
|
||||
} else if tracer != nil {
|
||||
vmConfig.Tracer = tracer.Hooks
|
||||
}
|
||||
// Run the test and aggregate the result
|
||||
s, result, body, err := prestate.Apply(vmConfig, chainConfig, txIt, ctx.Int64(RewardFlag.Name))
|
||||
|
|
@ -212,9 +213,13 @@ func Transition(ctx *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
// Write opcode counts if enabled
|
||||
if counter != nil {
|
||||
if opcodeTracer != nil {
|
||||
fname := ctx.String(OpcodeCountFlag.Name)
|
||||
if err := saveFile(baseDir, fname, counter.Results()); err != nil {
|
||||
result, err := opcodeTracer.GetResult()
|
||||
if err != nil {
|
||||
return NewError(ErrorJson, fmt.Errorf("failed getting opcode counts: %v", err))
|
||||
}
|
||||
if err := saveFile(baseDir, fname, result); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,30 +17,39 @@
|
|||
package native
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||
)
|
||||
|
||||
// OpcodeCounter is a simple tracer that counts how many times each opcode is executed.
|
||||
type OpcodeCounter struct {
|
||||
// opcodeCounter is a simple tracer that counts how many times each opcode is executed.
|
||||
type opcodeCounter struct {
|
||||
counts [256]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[op]++
|
||||
// NewOpcodeCounter returns a new opcodeCounter tracer.
|
||||
func NewOpcodeCounter() *tracers.Tracer {
|
||||
c := &opcodeCounter{}
|
||||
return &tracers.Tracer{
|
||||
Hooks: &tracing.Hooks{
|
||||
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||
c.counts[op]++
|
||||
},
|
||||
},
|
||||
GetResult: c.getResult,
|
||||
Stop: func(err error) {},
|
||||
}
|
||||
}
|
||||
|
||||
// Results returns the opcode counts keyed by opcode name.
|
||||
func (c *OpcodeCounter) Results() map[string]uint64 {
|
||||
out := make(map[string]uint64, len(c.counts))
|
||||
// getResult returns the opcode counts keyed by opcode name.
|
||||
func (c *opcodeCounter) getResult() (json.RawMessage, error) {
|
||||
out := make(map[string]uint64)
|
||||
for op, count := range c.counts {
|
||||
if count != 0 {
|
||||
out[vm.OpCode(op).String()] = count
|
||||
}
|
||||
}
|
||||
return out
|
||||
return json.Marshal(out)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue