From 567f69539e489f072f6814e9469a920f7fc5c3d4 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Fri, 2 Jun 2017 10:53:55 +0200 Subject: [PATCH] cmd/evm: better json output, add result to json --- cmd/evm/runner.go | 16 ++++++---- core/vm/logger.go | 63 ++++++++++++++++++++++++++++++++++++++- internal/ethapi/tracer.go | 7 +++++ 3 files changed, 79 insertions(+), 7 deletions(-) diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 33dd58b5d3..a9e032b1a9 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -86,8 +86,9 @@ func runCmd(ctx *cli.Context) error { } else if ctx.GlobalBool(DebugFlag.Name) { debugLogger = vm.NewStructLogger(nil) tracer = debugLogger + } else { + debugLogger = vm.NewStructLogger(nil) } - if ctx.GlobalString(GenesisFlag.Name) != "" { gen := readGenesis(ctx.GlobalString(GenesisFlag.Name)) _, statedb = gen.ToBlock() @@ -216,11 +217,14 @@ GC calls: %d `, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC) } - - fmt.Printf("0x%x", ret) - if err != nil { - fmt.Printf(" error: %v", err) + if tracer != nil { + tracer.CaptureEnd(ret, 0, execTime) + } else { + fmt.Printf("0x%x\n", ret) + } + + if err != nil { + fmt.Printf(" error: %v\n", err) } - fmt.Println() return nil } diff --git a/core/vm/logger.go b/core/vm/logger.go index 0f17840bf5..d8f550b374 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -22,6 +22,7 @@ import ( "fmt" "io" "math/big" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" @@ -61,6 +62,50 @@ type StructLog struct { Depth int Err error } +type StructLogJsonOut struct { + Pc uint64 `json:"pc"` + Op OpCode `json:"op"` + OpName string `json:"opName"` + Gas math.HexOrDecimal64 `json:"gas"` + GasCost math.HexOrDecimal64 `json:"gasCost"` + Memory string `json:"memory"` + Stack hexArray `json:"stack"` + //Storage map[common.Hash]common.Hash `json:"storage"` + Depth int `json:"depth"` + //Err error +} +type hexArray []*big.Int + +// MarshalJSON encodes the memory as 32-byte hex strings instead of bigints +func (a hexArray) MarshalJSON() ([]byte, error) { + + if items := ([]*big.Int)(a); len(items) > 0 { + hexitems := make([]*math.HexOrDecimal256, len(items)) + for i, item := range items { + x := math.HexOrDecimal256(*item) + hexitems[i] = &x + } + return json.Marshal(hexitems) + } + return []byte("[]"), nil +} + +// MarshalJSON encodes StructLog for json output +func (s StructLog) MarshalJSON() ([]byte, error) { + var enc StructLogJsonOut + enc.Pc = s.Pc + enc.Op = s.Op + enc.OpName = s.Op.String() + enc.Gas = math.HexOrDecimal64(s.Gas) + enc.GasCost = math.HexOrDecimal64(s.GasCost) + //enc.Memory = fmt.Sprintf("0x%v", len(common.Bytes2Hex(s.Memory))/2) + enc.Memory = fmt.Sprintf("%v bytes", len(s.Memory)) + + enc.Stack = s.Stack + //enc.Storage = s.Storage + enc.Depth = s.Depth + return json.Marshal(&enc) +} // Tracer is used to collect execution traces from an EVM transaction // execution. CaptureState is called for each step of the VM with the @@ -69,6 +114,7 @@ type StructLog struct { // if you need to retain them beyond the current call. type Tracer interface { CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error + CaptureEnd(output []byte, gasUsed uint64, t time.Duration) error } // StructLogger is an EVM state logger and implements Tracer. @@ -110,9 +156,20 @@ func NewJSONLogger(writer io.Writer) *JSONLogger { // CaptureState outputs state information on the logger func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { - log := StructLog{pc, op, gas, cost, memory.Data(), stack.Data(), nil, env.depth, err} + log := StructLog{pc, op, gas + cost, cost, memory.Data(), stack.Data(), nil, env.depth, err} return l.encoder.Encode(log) } +func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration) error { + type endLog struct { + Output string `json:"output"` + GasUsed math.HexOrDecimal64 `json:"gasUsed"` + Time time.Duration `json:"time"` + } + + log := endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t} + return l.encoder.Encode(log) + +} // CaptureState logs a new structured log message and pushes it out to the environment // @@ -184,6 +241,10 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui l.logs = append(l.logs, log) return nil } +func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration) error { + fmt.Printf("0x%x", output) + return nil +} // StructLogs returns a list of captured log entries func (l *StructLogger) StructLogs() []StructLog { diff --git a/internal/ethapi/tracer.go b/internal/ethapi/tracer.go index d343635641..fc66839ea5 100644 --- a/internal/ethapi/tracer.go +++ b/internal/ethapi/tracer.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "math/big" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -344,6 +345,12 @@ func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, return nil } +// CaptureEnd is called after the call finishes +func (jst *JavascriptTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration) error { + //TODO! @Arachnid please figure out of there's anything we can use this method for + return nil +} + // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error func (jst *JavascriptTracer) GetResult() (result interface{}, err error) { if jst.err != nil {