github.com/devfans/go-ethereum@v1.5.10-0.20170326212234-7419d0c38291/core/vm/logger.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package vm
    18  
    19  import (
    20  	"encoding/hex"
    21  	"fmt"
    22  	"io"
    23  	"math/big"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/common/math"
    27  	"github.com/ethereum/go-ethereum/core/types"
    28  )
    29  
    30  type Storage map[common.Hash]common.Hash
    31  
    32  func (self Storage) Copy() Storage {
    33  	cpy := make(Storage)
    34  	for key, value := range self {
    35  		cpy[key] = value
    36  	}
    37  
    38  	return cpy
    39  }
    40  
    41  // LogConfig are the configuration options for structured logger the EVM
    42  type LogConfig struct {
    43  	DisableMemory  bool // disable memory capture
    44  	DisableStack   bool // disable stack capture
    45  	DisableStorage bool // disable storage capture
    46  	FullStorage    bool // show full storage (slow)
    47  	Limit          int  // maximum length of output, but zero means unlimited
    48  }
    49  
    50  // StructLog is emitted to the EVM each cycle and lists information about the current internal state
    51  // prior to the execution of the statement.
    52  type StructLog struct {
    53  	Pc      uint64
    54  	Op      OpCode
    55  	Gas     uint64
    56  	GasCost uint64
    57  	Memory  []byte
    58  	Stack   []*big.Int
    59  	Storage map[common.Hash]common.Hash
    60  	Depth   int
    61  	Err     error
    62  }
    63  
    64  // Tracer is used to collect execution traces from an EVM transaction
    65  // execution. CaptureState is called for each step of the VM with the
    66  // current VM state.
    67  // Note that reference types are actual VM data structures; make copies
    68  // if you need to retain them beyond the current call.
    69  type Tracer interface {
    70  	CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
    71  }
    72  
    73  // StructLogger is an EVM state logger and implements Tracer.
    74  //
    75  // StructLogger can capture state based on the given Log configuration and also keeps
    76  // a track record of modified storage which is used in reporting snapshots of the
    77  // contract their storage.
    78  type StructLogger struct {
    79  	cfg LogConfig
    80  
    81  	logs          []StructLog
    82  	changedValues map[common.Address]Storage
    83  }
    84  
    85  // NewLogger returns a new logger
    86  func NewStructLogger(cfg *LogConfig) *StructLogger {
    87  	logger := &StructLogger{
    88  		changedValues: make(map[common.Address]Storage),
    89  	}
    90  	if cfg != nil {
    91  		logger.cfg = *cfg
    92  	}
    93  	return logger
    94  }
    95  
    96  // captureState logs a new structured log message and pushes it out to the environment
    97  //
    98  // captureState also tracks SSTORE ops to track dirty values.
    99  func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
   100  	// check if already accumulated the specified number of logs
   101  	if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
   102  		return ErrTraceLimitReached
   103  	}
   104  
   105  	// initialise new changed values storage container for this contract
   106  	// if not present.
   107  	if l.changedValues[contract.Address()] == nil {
   108  		l.changedValues[contract.Address()] = make(Storage)
   109  	}
   110  
   111  	// capture SSTORE opcodes and determine the changed value and store
   112  	// it in the local storage container. NOTE: we do not need to do any
   113  	// range checks here because that's already handler prior to calling
   114  	// this function.
   115  	switch op {
   116  	case SSTORE:
   117  		var (
   118  			value   = common.BigToHash(stack.data[stack.len()-2])
   119  			address = common.BigToHash(stack.data[stack.len()-1])
   120  		)
   121  		l.changedValues[contract.Address()][address] = value
   122  	}
   123  
   124  	// copy a snapstot of the current memory state to a new buffer
   125  	var mem []byte
   126  	if !l.cfg.DisableMemory {
   127  		mem = make([]byte, len(memory.Data()))
   128  		copy(mem, memory.Data())
   129  	}
   130  
   131  	// copy a snapshot of the current stack state to a new buffer
   132  	var stck []*big.Int
   133  	if !l.cfg.DisableStack {
   134  		stck = make([]*big.Int, len(stack.Data()))
   135  		for i, item := range stack.Data() {
   136  			stck[i] = new(big.Int).Set(item)
   137  		}
   138  	}
   139  
   140  	// Copy the storage based on the settings specified in the log config. If full storage
   141  	// is disabled (default) we can use the simple Storage.Copy method, otherwise we use
   142  	// the state object to query for all values (slow process).
   143  	var storage Storage
   144  	if !l.cfg.DisableStorage {
   145  		if l.cfg.FullStorage {
   146  			storage = make(Storage)
   147  			// Get the contract account and loop over each storage entry. This may involve looping over
   148  			// the trie and is a very expensive process.
   149  
   150  			env.StateDB.ForEachStorage(contract.Address(), func(key, value common.Hash) bool {
   151  				storage[key] = value
   152  				// Return true, indicating we'd like to continue.
   153  				return true
   154  			})
   155  		} else {
   156  			// copy a snapshot of the current storage to a new container.
   157  			storage = l.changedValues[contract.Address()].Copy()
   158  		}
   159  	}
   160  	// create a new snaptshot of the EVM.
   161  	log := StructLog{pc, op, gas, cost, mem, stck, storage, env.depth, err}
   162  
   163  	l.logs = append(l.logs, log)
   164  	return nil
   165  }
   166  
   167  // StructLogs returns a list of captured log entries
   168  func (l *StructLogger) StructLogs() []StructLog {
   169  	return l.logs
   170  }
   171  
   172  // WriteTrace writes a formatted trace to the given writer
   173  func WriteTrace(writer io.Writer, logs []StructLog) {
   174  	for _, log := range logs {
   175  		fmt.Fprintf(writer, "%-10spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost)
   176  		if log.Err != nil {
   177  			fmt.Fprintf(writer, " ERROR: %v", log.Err)
   178  		}
   179  		fmt.Fprintf(writer, "\n")
   180  
   181  		for i := len(log.Stack) - 1; i >= 0; i-- {
   182  			fmt.Fprintf(writer, "%08d  %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
   183  		}
   184  
   185  		fmt.Fprint(writer, hex.Dump(log.Memory))
   186  
   187  		for h, item := range log.Storage {
   188  			fmt.Fprintf(writer, "%x: %x\n", h, item)
   189  		}
   190  		fmt.Fprintln(writer)
   191  	}
   192  }
   193  
   194  // WriteLogs writes vm logs in a readable format to the given writer
   195  func WriteLogs(writer io.Writer, logs []*types.Log) {
   196  	for _, log := range logs {
   197  		fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex)
   198  
   199  		for i, topic := range log.Topics {
   200  			fmt.Fprintf(writer, "%08d  %x\n", i, topic)
   201  		}
   202  
   203  		fmt.Fprint(writer, hex.Dump(log.Data))
   204  		fmt.Fprintln(writer)
   205  	}
   206  }