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 }