From 373f587d8a5951f74e45570a833e2193d9944eb4 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Wed, 7 Jun 2017 11:14:20 +0200 Subject: [PATCH] core/vm: use json codec to generate structlog json --- cmd/evm/json_logger.go | 69 +++++++++++++++++++++++++ cmd/evm/runner.go | 2 +- core/vm/gen_structlog.go | 94 +++++++++++++++++++++++++++++++++++ core/vm/logger.go | 105 +++++++++------------------------------ 4 files changed, 187 insertions(+), 83 deletions(-) create mode 100644 cmd/evm/json_logger.go create mode 100644 core/vm/gen_structlog.go diff --git a/cmd/evm/json_logger.go b/cmd/evm/json_logger.go new file mode 100644 index 0000000000..420b516763 --- /dev/null +++ b/cmd/evm/json_logger.go @@ -0,0 +1,69 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package main + +import ( + "encoding/json" + "io" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/vm" +) + +// JSONLogger merely contains a writer, and immediately outputs to that channel, +// instead of collecting logs +type JSONLogger struct { + encoder *json.Encoder +} + +// NewJSONLogger returns a new JSON logger +func NewJSONLogger(writer io.Writer) *JSONLogger { + logger := &JSONLogger{ + encoder: json.NewEncoder(writer), + } + return logger +} + +// CaptureState outputs state information on the logger +func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { + log := vm.StructLog{ + Pc: pc, + Op: op, + Gas: gas + cost, + GasCost: cost, + Memory: memory.Data(), + Stack: stack.Data(), + Storage: nil, + Depth: depth, + Err: err} + return l.encoder.Encode(log) +} + +// CaptureEnd is triggered at end of execution +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) + +} diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 90ed42897d..b1fb8998f1 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -82,7 +82,7 @@ func runCmd(ctx *cli.Context) error { sender = common.StringToAddress("sender") ) if ctx.GlobalBool(MachineFlag.Name) { - tracer = vm.NewJSONLogger(os.Stdout) + tracer = NewJSONLogger(os.Stdout) } else if ctx.GlobalBool(DebugFlag.Name) { debugLogger = vm.NewStructLogger(nil) tracer = debugLogger diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go new file mode 100644 index 0000000000..fec0e36196 --- /dev/null +++ b/core/vm/gen_structlog.go @@ -0,0 +1,94 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package vm + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" +) + +func (s StructLog) MarshalJSON() ([]byte, error) { + type StructLog struct { + Pc uint64 `json:"pc"` + Op OpCode `json:"op"` + Gas math.HexOrDecimal64 `json:"gas"` + GasCost math.HexOrDecimal64 `json:"gasCost"` + Memory []byte `json:"memory"` + Stack []*math.HexOrDecimal256 `json:"stack"` + Storage map[common.Hash]common.Hash `json:-` + Depth int `json:"depth"` + Err error `json:"error"` + OpName string `json:"opName"` + MemorySize string `json:"memSize"` + } + var enc StructLog + enc.Pc = s.Pc + enc.Op = s.Op + enc.Gas = math.HexOrDecimal64(s.Gas) + enc.GasCost = math.HexOrDecimal64(s.GasCost) + enc.Memory = s.Memory + if s.Stack != nil { + enc.Stack = make([]*math.HexOrDecimal256, len(s.Stack)) + for k, v := range s.Stack { + enc.Stack[k] = (*math.HexOrDecimal256)(v) + } + } + enc.Storage = s.Storage + enc.Depth = s.Depth + enc.Err = s.Err + enc.OpName = s.OpName() + enc.MemorySize = s.MemorySize() + return json.Marshal(&enc) +} + +func (s *StructLog) UnmarshalJSON(input []byte) error { + type StructLog struct { + Pc *uint64 `json:"pc"` + Op *OpCode `json:"op"` + Gas *math.HexOrDecimal64 `json:"gas"` + GasCost *math.HexOrDecimal64 `json:"gasCost"` + Memory []byte `json:"memory"` + Stack []*math.HexOrDecimal256 `json:"stack"` + Storage map[common.Hash]common.Hash `json:-` + Depth *int `json:"depth"` + Err *error `json:"error"` + } + var dec StructLog + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Pc != nil { + s.Pc = *dec.Pc + } + if dec.Op != nil { + s.Op = *dec.Op + } + if dec.Gas != nil { + s.Gas = uint64(*dec.Gas) + } + if dec.GasCost != nil { + s.GasCost = uint64(*dec.GasCost) + } + if dec.Memory != nil { + s.Memory = dec.Memory + } + if dec.Stack != nil { + s.Stack = make([]*big.Int, len(dec.Stack)) + for k, v := range dec.Stack { + s.Stack[k] = (*big.Int)(v) + } + } + if dec.Storage != nil { + s.Storage = dec.Storage + } + if dec.Depth != nil { + s.Depth = *dec.Depth + } + if dec.Err != nil { + s.Err = *dec.Err + } + return nil +} diff --git a/core/vm/logger.go b/core/vm/logger.go index d8f550b374..df9e3a514e 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -18,7 +18,6 @@ package vm import ( "encoding/hex" - "encoding/json" "fmt" "io" "math/big" @@ -51,60 +50,33 @@ type LogConfig struct { // StructLog is emitted to the EVM each cycle and lists information about the current internal state // prior to the execution of the statement. +//go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go type StructLog struct { - Pc uint64 - Op OpCode - Gas uint64 - GasCost uint64 - Memory []byte - Stack []*big.Int - Storage map[common.Hash]common.Hash - 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 + Pc uint64 `json:"pc"` + Op OpCode `json:"op"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` + Memory []byte `json:"memory"` + Stack []*big.Int `json:"stack"` + Storage map[common.Hash]common.Hash `json:-` + Depth int `json:"depth"` + Err error `json:"error"` } -// 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)) +func (s *StructLog) OpName() string { + return s.Op.String() +} - enc.Stack = s.Stack - //enc.Storage = s.Storage - enc.Depth = s.Depth - return json.Marshal(&enc) +func (s *StructLog) MemorySize() string { + return fmt.Sprintf("%v", len(s.Memory)) +} + +type structLogMarshaling struct { + Stack []*math.HexOrDecimal256 + Gas math.HexOrDecimal64 + GasCost math.HexOrDecimal64 + OpName string `json:"opName"` + MemorySize string `json:"memSize"` } // Tracer is used to collect execution traces from an EVM transaction @@ -129,12 +101,6 @@ type StructLogger struct { changedValues map[common.Address]Storage } -// JSONLogger merely contains a writer, and immediately outputs to that channel, -// instead of collecting logs -type JSONLogger struct { - encoder *json.Encoder -} - // NewStructLogger returns a new logger func NewStructLogger(cfg *LogConfig) *StructLogger { logger := &StructLogger{ @@ -146,31 +112,6 @@ func NewStructLogger(cfg *LogConfig) *StructLogger { return logger } -// NewJSONLogger returns a new JSON logger -func NewJSONLogger(writer io.Writer) *JSONLogger { - logger := &JSONLogger{ - encoder: json.NewEncoder(writer), - } - return logger -} - -// 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, 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 // // CaptureState also tracks SSTORE ops to track dirty values.