mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-19 11:20:45 +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
|
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.
|
// 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{
|
t := &fileWritingTracer{
|
||||||
baseDir: baseDir,
|
baseDir: baseDir,
|
||||||
suffix: "jsonl",
|
suffix: "jsonl",
|
||||||
}
|
}
|
||||||
t.inner = innerFn(t) // instantiate the inner tracer
|
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.
|
// 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{
|
t := &fileWritingTracer{
|
||||||
baseDir: baseDir,
|
baseDir: baseDir,
|
||||||
getResult: tracer.GetResult,
|
getResult: tracer.GetResult,
|
||||||
inner: tracer.Hooks,
|
inner: tracer.Hooks,
|
||||||
suffix: "json",
|
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
|
// OnTxStart creates a new output-file specific for this transaction, and invokes
|
||||||
|
|
|
||||||
|
|
@ -168,14 +168,15 @@ func Transition(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure tracer
|
// Configure tracer
|
||||||
|
var tracer *tracers.Tracer
|
||||||
if ctx.IsSet(TraceTracerFlag.Name) { // Custom tracing
|
if ctx.IsSet(TraceTracerFlag.Name) { // Custom tracing
|
||||||
config := json.RawMessage(ctx.String(TraceTracerConfigFlag.Name))
|
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)
|
nil, config, chainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %v", err))
|
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
|
} else if ctx.Bool(TraceFlag.Name) { // JSON opcode tracing
|
||||||
logConfig := &logger.Config{
|
logConfig := &logger.Config{
|
||||||
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
|
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
|
||||||
|
|
@ -183,28 +184,28 @@ func Transition(ctx *cli.Context) error {
|
||||||
EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
|
EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
|
||||||
}
|
}
|
||||||
if ctx.Bool(TraceEnableCallFramesFlag.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)
|
return logger.NewJSONLoggerWithCallFrames(logConfig, out)
|
||||||
})
|
})
|
||||||
} else {
|
} 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)
|
return logger.NewJSONLogger(logConfig, out)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Configure opcode counter
|
// Configure opcode counter
|
||||||
var counter *native.OpcodeCounter
|
var opcodeTracer *tracers.Tracer
|
||||||
if ctx.IsSet(OpcodeCountFlag.Name) && ctx.String(OpcodeCountFlag.Name) != "" {
|
if ctx.IsSet(OpcodeCountFlag.Name) && ctx.String(OpcodeCountFlag.Name) != "" {
|
||||||
counter = new(native.OpcodeCounter)
|
opcodeTracer = native.NewOpcodeCounter()
|
||||||
if vmConfig.Tracer != nil {
|
if tracer != nil {
|
||||||
// If we have an existing tracer, multiplex with the opcode tracer
|
// If we have an existing tracer, multiplex with the opcode tracer
|
||||||
tExisting := tracers.Tracer{Hooks: vmConfig.Tracer}
|
mux, _ := native.NewMuxTracer([]string{"trace", "opcode"}, []*tracers.Tracer{tracer, opcodeTracer})
|
||||||
tCounter := tracers.Tracer{Hooks: counter.Hooks()}
|
vmConfig.Tracer = mux.Hooks
|
||||||
muxTracer, _ := native.NewMuxTracer([]string{"existing", "opcode"}, []*tracers.Tracer{&tExisting, &tCounter})
|
|
||||||
vmConfig.Tracer = muxTracer.Hooks
|
|
||||||
} else {
|
} else {
|
||||||
vmConfig.Tracer = counter.Hooks()
|
vmConfig.Tracer = opcodeTracer.Hooks
|
||||||
}
|
}
|
||||||
|
} else if tracer != nil {
|
||||||
|
vmConfig.Tracer = tracer.Hooks
|
||||||
}
|
}
|
||||||
// Run the test and aggregate the result
|
// Run the test and aggregate the result
|
||||||
s, result, body, err := prestate.Apply(vmConfig, chainConfig, txIt, ctx.Int64(RewardFlag.Name))
|
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
|
return err
|
||||||
}
|
}
|
||||||
// Write opcode counts if enabled
|
// Write opcode counts if enabled
|
||||||
if counter != nil {
|
if opcodeTracer != nil {
|
||||||
fname := ctx.String(OpcodeCountFlag.Name)
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,30 +17,39 @@
|
||||||
package native
|
package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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.
|
// opcodeCounter is a simple tracer that counts how many times each opcode is executed.
|
||||||
type OpcodeCounter struct {
|
type opcodeCounter struct {
|
||||||
counts [256]uint64
|
counts [256]uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OpcodeCounter) Hooks() *tracing.Hooks {
|
// NewOpcodeCounter returns a new opcodeCounter tracer.
|
||||||
return &tracing.Hooks{
|
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) {
|
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||||
c.counts[op]++
|
c.counts[op]++
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
GetResult: c.getResult,
|
||||||
|
Stop: func(err error) {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Results returns the opcode counts keyed by opcode name.
|
// getResult returns the opcode counts keyed by opcode name.
|
||||||
func (c *OpcodeCounter) Results() map[string]uint64 {
|
func (c *opcodeCounter) getResult() (json.RawMessage, error) {
|
||||||
out := make(map[string]uint64, len(c.counts))
|
out := make(map[string]uint64)
|
||||||
for op, count := range c.counts {
|
for op, count := range c.counts {
|
||||||
if count != 0 {
|
if count != 0 {
|
||||||
out[vm.OpCode(op).String()] = count
|
out[vm.OpCode(op).String()] = count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out
|
return json.Marshal(out)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue