github.com/digdeepmining/go-atheios@v1.5.13-0.20180902133602-d5687a2e6f43/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  	"fmt"
    21  	"math/big"
    22  	"os"
    23  	"unicode"
    24  
    25  	"github.com/atheioschain/go-atheios/common"
    26  )
    27  
    28  type Storage map[common.Hash]common.Hash
    29  
    30  func (self Storage) Copy() Storage {
    31  	cpy := make(Storage)
    32  	for key, value := range self {
    33  		cpy[key] = value
    34  	}
    35  
    36  	return cpy
    37  }
    38  
    39  // LogConfig are the configuration options for structured logger the EVM
    40  type LogConfig struct {
    41  	DisableMemory  bool // disable memory capture
    42  	DisableStack   bool // disable stack capture
    43  	DisableStorage bool // disable storage capture
    44  	FullStorage    bool // show full storage (slow)
    45  	Limit          int  // maximum length of output, but zero means unlimited
    46  }
    47  
    48  // StructLog is emitted to the EVM each cycle and lists information about the current internal state
    49  // prior to the execution of the statement.
    50  type StructLog struct {
    51  	Pc      uint64
    52  	Op      OpCode
    53  	Gas     *big.Int
    54  	GasCost *big.Int
    55  	Memory  []byte
    56  	Stack   []*big.Int
    57  	Storage map[common.Hash]common.Hash
    58  	Depth   int
    59  	Err     error
    60  }
    61  
    62  // Tracer is used to collect execution traces from an EVM transaction
    63  // execution. CaptureState is called for each step of the VM with the
    64  // current VM state.
    65  // Note that reference types are actual VM data structures; make copies
    66  // if you need to retain them beyond the current call.
    67  type Tracer interface {
    68  	CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
    69  }
    70  
    71  // StructLogger is an EVM state logger and implements Tracer.
    72  //
    73  // StructLogger can capture state based on the given Log configuration and also keeps
    74  // a track record of modified storage which is used in reporting snapshots of the
    75  // contract their storage.
    76  type StructLogger struct {
    77  	cfg LogConfig
    78  
    79  	logs          []StructLog
    80  	changedValues map[common.Address]Storage
    81  }
    82  
    83  // NewLogger returns a new logger
    84  func NewStructLogger(cfg *LogConfig) *StructLogger {
    85  	logger := &StructLogger{
    86  		changedValues: make(map[common.Address]Storage),
    87  	}
    88  	if cfg != nil {
    89  		logger.cfg = *cfg
    90  	}
    91  	return logger
    92  }
    93  
    94  // captureState logs a new structured log message and pushes it out to the environment
    95  //
    96  // captureState also tracks SSTORE ops to track dirty values.
    97  func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
    98  	// check if already accumulated the specified number of logs
    99  	if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
   100  		return ErrTraceLimitReached
   101  	}
   102  
   103  	// initialise new changed values storage container for this contract
   104  	// if not present.
   105  	if l.changedValues[contract.Address()] == nil {
   106  		l.changedValues[contract.Address()] = make(Storage)
   107  	}
   108  
   109  	// capture SSTORE opcodes and determine the changed value and store
   110  	// it in the local storage container. NOTE: we do not need to do any
   111  	// range checks here because that's already handler prior to calling
   112  	// this function.
   113  	switch op {
   114  	case SSTORE:
   115  		var (
   116  			value   = common.BigToHash(stack.data[stack.len()-2])
   117  			address = common.BigToHash(stack.data[stack.len()-1])
   118  		)
   119  		l.changedValues[contract.Address()][address] = value
   120  	}
   121  
   122  	// copy a snapstot of the current memory state to a new buffer
   123  	var mem []byte
   124  	if !l.cfg.DisableMemory {
   125  		mem = make([]byte, len(memory.Data()))
   126  		copy(mem, memory.Data())
   127  	}
   128  
   129  	// copy a snapshot of the current stack state to a new buffer
   130  	var stck []*big.Int
   131  	if !l.cfg.DisableStack {
   132  		stck = make([]*big.Int, len(stack.Data()))
   133  		for i, item := range stack.Data() {
   134  			stck[i] = new(big.Int).Set(item)
   135  		}
   136  	}
   137  
   138  	// Copy the storage based on the settings specified in the log config. If full storage
   139  	// is disabled (default) we can use the simple Storage.Copy method, otherwise we use
   140  	// the state object to query for all values (slow process).
   141  	var storage Storage
   142  	if !l.cfg.DisableStorage {
   143  		if l.cfg.FullStorage {
   144  			storage = make(Storage)
   145  			// Get the contract account and loop over each storage entry. This may involve looping over
   146  			// the trie and is a very expensive process.
   147  			env.StateDB.GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool {
   148  				storage[key] = value
   149  				// Return true, indicating we'd like to continue.
   150  				return true
   151  			})
   152  		} else {
   153  			// copy a snapshot of the current storage to a new container.
   154  			storage = l.changedValues[contract.Address()].Copy()
   155  		}
   156  	}
   157  	// create a new snaptshot of the EVM.
   158  	log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.depth, err}
   159  
   160  	l.logs = append(l.logs, log)
   161  	return nil
   162  }
   163  
   164  // StructLogs returns a list of captured log entries
   165  func (l *StructLogger) StructLogs() []StructLog {
   166  	return l.logs
   167  }
   168  
   169  // StdErrFormat formats a slice of StructLogs to human readable format
   170  func StdErrFormat(logs []StructLog) {
   171  	fmt.Fprintf(os.Stderr, "VM STAT %d OPs\n", len(logs))
   172  	for _, log := range logs {
   173  		fmt.Fprintf(os.Stderr, "PC %08d: %s GAS: %v COST: %v", log.Pc, log.Op, log.Gas, log.GasCost)
   174  		if log.Err != nil {
   175  			fmt.Fprintf(os.Stderr, " ERROR: %v", log.Err)
   176  		}
   177  		fmt.Fprintf(os.Stderr, "\n")
   178  
   179  		fmt.Fprintln(os.Stderr, "STACK =", len(log.Stack))
   180  
   181  		for i := len(log.Stack) - 1; i >= 0; i-- {
   182  			fmt.Fprintf(os.Stderr, "%04d: %x\n", len(log.Stack)-i-1, common.LeftPadBytes(log.Stack[i].Bytes(), 32))
   183  		}
   184  
   185  		const maxMem = 10
   186  		addr := 0
   187  		fmt.Fprintln(os.Stderr, "MEM =", len(log.Memory))
   188  		for i := 0; i+16 <= len(log.Memory) && addr < maxMem; i += 16 {
   189  			data := log.Memory[i : i+16]
   190  			str := fmt.Sprintf("%04d: % x  ", addr*16, data)
   191  			for _, r := range data {
   192  				if r == 0 {
   193  					str += "."
   194  				} else if unicode.IsPrint(rune(r)) {
   195  					str += fmt.Sprintf("%s", string(r))
   196  				} else {
   197  					str += "?"
   198  				}
   199  			}
   200  			addr++
   201  			fmt.Fprintln(os.Stderr, str)
   202  		}
   203  
   204  		fmt.Fprintln(os.Stderr, "STORAGE =", len(log.Storage))
   205  		for h, item := range log.Storage {
   206  			fmt.Fprintf(os.Stderr, "%x: %x\n", h, item)
   207  		}
   208  		fmt.Fprintln(os.Stderr)
   209  	}
   210  }