github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/cmd/mist/debugger.go (about)

     1  /*
     2  	This file is part of go-ethereum
     3  
     4  	go-ethereum is free software: you can redistribute it and/or modify
     5  	it under the terms of the GNU 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  	go-ethereum 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 General Public License for more details.
    13  
    14  	You should have received a copy of the GNU General Public License
    15  	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
    16  */
    17  /**
    18   * @authors
    19   * 	Jeffrey Wilcke <i@jev.io>
    20   */
    21  package main
    22  
    23  import (
    24  	"fmt"
    25  	"math/big"
    26  	"strconv"
    27  	"strings"
    28  	"unicode"
    29  
    30  	"github.com/jonasnick/go-ethereum/cmd/utils"
    31  	"github.com/jonasnick/go-ethereum/core"
    32  	"github.com/jonasnick/go-ethereum/core/types"
    33  	"github.com/jonasnick/go-ethereum/ethutil"
    34  	"github.com/jonasnick/go-ethereum/state"
    35  	"github.com/jonasnick/go-ethereum/vm"
    36  	"github.com/obscuren/qml"
    37  )
    38  
    39  type DebuggerWindow struct {
    40  	win    *qml.Window
    41  	engine *qml.Engine
    42  	lib    *UiLib
    43  
    44  	vm *vm.Vm
    45  	Db *Debugger
    46  
    47  	state *state.StateDB
    48  }
    49  
    50  func NewDebuggerWindow(lib *UiLib) *DebuggerWindow {
    51  	engine := qml.NewEngine()
    52  	component, err := engine.LoadFile(lib.AssetPath("debugger/debugger.qml"))
    53  	if err != nil {
    54  		fmt.Println(err)
    55  
    56  		return nil
    57  	}
    58  
    59  	win := component.CreateWindow(nil)
    60  
    61  	w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: &vm.Vm{}}
    62  	w.Db = NewDebugger(w)
    63  
    64  	return w
    65  }
    66  
    67  func (self *DebuggerWindow) Show() {
    68  	context := self.engine.Context()
    69  	context.SetVar("dbg", self)
    70  
    71  	go func() {
    72  		self.win.Show()
    73  		self.win.Wait()
    74  	}()
    75  }
    76  
    77  func (self *DebuggerWindow) SetCode(code string) {
    78  	self.win.Set("codeText", code)
    79  }
    80  
    81  func (self *DebuggerWindow) SetData(data string) {
    82  	self.win.Set("dataText", data)
    83  }
    84  
    85  func (self *DebuggerWindow) SetAsm(data []byte) {
    86  	self.win.Root().Call("clearAsm")
    87  
    88  	dis := core.Disassemble(data)
    89  	for _, str := range dis {
    90  		self.win.Root().Call("setAsm", str)
    91  	}
    92  }
    93  
    94  func (self *DebuggerWindow) Compile(code string) {
    95  	var err error
    96  	script := ethutil.StringToByteFunc(code, func(s string) (ret []byte) {
    97  		ret, err = ethutil.Compile(s, true)
    98  		return
    99  	})
   100  
   101  	if err == nil {
   102  		self.SetAsm(script)
   103  	}
   104  }
   105  
   106  // Used by QML
   107  func (self *DebuggerWindow) AutoComp(code string) {
   108  	if self.Db.done {
   109  		self.Compile(code)
   110  	}
   111  }
   112  
   113  func (self *DebuggerWindow) ClearLog() {
   114  	self.win.Root().Call("clearLog")
   115  }
   116  
   117  func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, dataStr string) {
   118  	self.Stop()
   119  
   120  	defer func() {
   121  		if r := recover(); r != nil {
   122  			self.Logf("compile FAULT: %v", r)
   123  		}
   124  	}()
   125  
   126  	data := utils.FormatTransactionData(dataStr)
   127  
   128  	var err error
   129  	script := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) {
   130  		ret, err = ethutil.Compile(s, false)
   131  		return
   132  	})
   133  
   134  	if err != nil {
   135  		self.Logln(err)
   136  
   137  		return
   138  	}
   139  
   140  	var (
   141  		gas      = ethutil.Big(gasStr)
   142  		gasPrice = ethutil.Big(gasPriceStr)
   143  		value    = ethutil.Big(valueStr)
   144  		// Contract addr as test address
   145  		keyPair = self.lib.eth.KeyManager().KeyPair()
   146  	)
   147  
   148  	statedb := self.lib.eth.ChainManager().TransState()
   149  	account := self.lib.eth.ChainManager().TransState().GetAccount(keyPair.Address())
   150  	contract := statedb.NewStateObject([]byte{0})
   151  	contract.SetCode(script)
   152  	contract.SetBalance(value)
   153  
   154  	self.SetAsm(script)
   155  
   156  	block := self.lib.eth.ChainManager().CurrentBlock()
   157  
   158  	msg := types.NewTransactionMessage(nil, value, gas, gasPrice, data)
   159  	env := core.NewEnv(statedb, self.lib.eth.ChainManager(), msg, block)
   160  
   161  	self.Logf("callsize %d", len(script))
   162  	go func() {
   163  		pgas := new(big.Int).Set(gas)
   164  		ret, err := env.Call(account, contract.Address(), data, gas, gasPrice, ethutil.Big0)
   165  
   166  		rgas := new(big.Int).Sub(pgas, gas)
   167  		tot := new(big.Int).Mul(rgas, gasPrice)
   168  		self.Logf("gas usage %v total price = %v (%v)", rgas, tot, ethutil.CurrencyToString(tot))
   169  		if err != nil {
   170  			self.Logln("exited with errors:", err)
   171  		} else {
   172  			if len(ret) > 0 {
   173  				self.Logf("exited: % x", ret)
   174  			} else {
   175  				self.Logf("exited: nil")
   176  			}
   177  		}
   178  
   179  		statedb.Reset()
   180  
   181  		if !self.Db.interrupt {
   182  			self.Db.done = true
   183  		} else {
   184  			self.Db.interrupt = false
   185  		}
   186  	}()
   187  }
   188  
   189  func (self *DebuggerWindow) Logf(format string, v ...interface{}) {
   190  	self.win.Root().Call("setLog", fmt.Sprintf(format, v...))
   191  }
   192  
   193  func (self *DebuggerWindow) Logln(v ...interface{}) {
   194  	str := fmt.Sprintln(v...)
   195  	self.Logf("%s", str[:len(str)-1])
   196  }
   197  
   198  func (self *DebuggerWindow) Next() {
   199  	self.Db.Next()
   200  }
   201  
   202  func (self *DebuggerWindow) Continue() {
   203  	self.vm.Stepping = false
   204  	self.Next()
   205  }
   206  
   207  func (self *DebuggerWindow) Stop() {
   208  	if !self.Db.done {
   209  		self.Db.Q <- true
   210  	}
   211  }
   212  
   213  func (self *DebuggerWindow) ExecCommand(command string) {
   214  	if len(command) > 0 {
   215  		cmd := strings.Split(command, " ")
   216  		switch cmd[0] {
   217  		case "help":
   218  			self.Logln("Debugger commands:")
   219  			self.Logln("break, bp                 Set breakpoint on instruction")
   220  			self.Logln("clear [log, break, bp]    Clears previous set sub-command(s)")
   221  		case "break", "bp":
   222  			if len(cmd) > 1 {
   223  				lineNo, err := strconv.Atoi(cmd[1])
   224  				if err != nil {
   225  					self.Logln(err)
   226  					break
   227  				}
   228  				self.Db.breakPoints = append(self.Db.breakPoints, int64(lineNo))
   229  				self.Logf("break point set on instruction %d", lineNo)
   230  			} else {
   231  				self.Logf("'%s' requires line number", cmd[0])
   232  			}
   233  		case "clear":
   234  			if len(cmd) > 1 {
   235  				switch cmd[1] {
   236  				case "break", "bp":
   237  					self.Db.breakPoints = nil
   238  
   239  					self.Logln("Breakpoints cleared")
   240  				case "log":
   241  					self.ClearLog()
   242  				default:
   243  					self.Logf("clear '%s' is not valid", cmd[1])
   244  				}
   245  			} else {
   246  				self.Logln("'clear' requires sub command")
   247  			}
   248  
   249  		default:
   250  			self.Logf("Unknown command %s", cmd[0])
   251  		}
   252  	}
   253  }
   254  
   255  type Debugger struct {
   256  	N               chan bool
   257  	Q               chan bool
   258  	done, interrupt bool
   259  	breakPoints     []int64
   260  	main            *DebuggerWindow
   261  	win             *qml.Window
   262  }
   263  
   264  func NewDebugger(main *DebuggerWindow) *Debugger {
   265  	db := &Debugger{make(chan bool), make(chan bool), true, false, nil, main, main.win}
   266  
   267  	return db
   268  }
   269  
   270  type storeVal struct {
   271  	Key, Value string
   272  }
   273  
   274  func (self *Debugger) Step(evm vm.VirtualMachine, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, context *vm.Context) {
   275  }
   276  
   277  func (self *Debugger) BreakHook(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool {
   278  	self.main.Logln("break on instr:", pc)
   279  
   280  	return self.halting(pc, op, mem, stack, stateObject)
   281  }
   282  
   283  func (self *Debugger) StepHook(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool {
   284  	return self.halting(pc, op, mem, stack, stateObject)
   285  }
   286  
   287  func (self *Debugger) SetCode(byteCode []byte) {
   288  	self.main.SetAsm(byteCode)
   289  }
   290  
   291  func (self *Debugger) BreakPoints() []int64 {
   292  	return self.breakPoints
   293  }
   294  
   295  func (d *Debugger) halting(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool {
   296  	d.win.Root().Call("setInstruction", pc)
   297  	d.win.Root().Call("clearMem")
   298  	d.win.Root().Call("clearStack")
   299  	d.win.Root().Call("clearStorage")
   300  
   301  	addr := 0
   302  	for i := 0; i+16 <= mem.Len(); i += 16 {
   303  		dat := mem.Data()[i : i+16]
   304  		var str string
   305  
   306  		for _, d := range dat {
   307  			if unicode.IsGraphic(rune(d)) {
   308  				str += string(d)
   309  			} else {
   310  				str += "?"
   311  			}
   312  		}
   313  
   314  		d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("%s  % x", str, dat)})
   315  		addr += 16
   316  	}
   317  
   318  	for _, val := range stack.Data() {
   319  		d.win.Root().Call("setStack", val.String())
   320  	}
   321  
   322  	it := stateObject.Trie().Iterator()
   323  	for it.Next() {
   324  		d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", it.Key), fmt.Sprintf("% x", it.Value)})
   325  
   326  	}
   327  
   328  	stackFrameAt := new(big.Int).SetBytes(mem.Get(0, 32))
   329  	psize := mem.Len() - int(new(big.Int).SetBytes(mem.Get(0, 32)).Uint64())
   330  	d.win.Root().ObjectByName("stackFrame").Set("text", fmt.Sprintf(`<b>stack ptr</b>: %v`, stackFrameAt))
   331  	d.win.Root().ObjectByName("stackSize").Set("text", fmt.Sprintf(`<b>stack size</b>: %d`, psize))
   332  	d.win.Root().ObjectByName("memSize").Set("text", fmt.Sprintf(`<b>mem size</b>: %v`, mem.Len()))
   333  
   334  out:
   335  	for {
   336  		select {
   337  		case <-d.N:
   338  			break out
   339  		case <-d.Q:
   340  			d.interrupt = true
   341  			d.clearBuffers()
   342  
   343  			return false
   344  		}
   345  	}
   346  
   347  	return true
   348  }
   349  
   350  func (d *Debugger) clearBuffers() {
   351  out:
   352  	// drain
   353  	for {
   354  		select {
   355  		case <-d.N:
   356  		case <-d.Q:
   357  		default:
   358  			break out
   359  		}
   360  	}
   361  }
   362  
   363  func (d *Debugger) Next() {
   364  	if !d.done {
   365  		d.N <- true
   366  	}
   367  }