debugger.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. package main
  2. import (
  3. "fmt"
  4. "math/big"
  5. "strconv"
  6. "strings"
  7. "github.com/ethereum/eth-go/ethchain"
  8. "github.com/ethereum/eth-go/ethstate"
  9. "github.com/ethereum/eth-go/ethutil"
  10. "github.com/ethereum/eth-go/ethvm"
  11. "github.com/ethereum/go-ethereum/utils"
  12. "gopkg.in/qml.v1"
  13. )
  14. type DebuggerWindow struct {
  15. win *qml.Window
  16. engine *qml.Engine
  17. lib *UiLib
  18. vm *ethvm.Vm
  19. Db *Debugger
  20. state *ethstate.State
  21. }
  22. func NewDebuggerWindow(lib *UiLib) *DebuggerWindow {
  23. engine := qml.NewEngine()
  24. component, err := engine.LoadFile(lib.AssetPath("debugger/debugger.qml"))
  25. if err != nil {
  26. fmt.Println(err)
  27. return nil
  28. }
  29. win := component.CreateWindow(nil)
  30. w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: &ethvm.Vm{}}
  31. w.Db = NewDebugger(w)
  32. return w
  33. }
  34. func (self *DebuggerWindow) Show() {
  35. context := self.engine.Context()
  36. context.SetVar("dbg", self)
  37. go func() {
  38. self.win.Show()
  39. self.win.Wait()
  40. }()
  41. }
  42. func (self *DebuggerWindow) SetCode(code string) {
  43. self.win.Set("codeText", code)
  44. }
  45. func (self *DebuggerWindow) SetData(data string) {
  46. self.win.Set("dataText", data)
  47. }
  48. func (self *DebuggerWindow) SetAsm(data []byte) {
  49. self.win.Root().Call("clearAsm")
  50. dis := ethchain.Disassemble(data)
  51. for _, str := range dis {
  52. self.win.Root().Call("setAsm", str)
  53. }
  54. }
  55. func (self *DebuggerWindow) Compile(code string) {
  56. var err error
  57. script := ethutil.StringToByteFunc(code, func(s string) (ret []byte) {
  58. ret, err = ethutil.Compile(s, true)
  59. return
  60. })
  61. if err == nil {
  62. self.SetAsm(script)
  63. }
  64. }
  65. // Used by QML
  66. func (self *DebuggerWindow) AutoComp(code string) {
  67. if self.Db.done {
  68. self.Compile(code)
  69. }
  70. }
  71. func (self *DebuggerWindow) ClearLog() {
  72. self.win.Root().Call("clearLog")
  73. }
  74. func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, dataStr string) {
  75. if !self.Db.done {
  76. self.Db.Q <- true
  77. }
  78. defer func() {
  79. if r := recover(); r != nil {
  80. self.Logf("compile FAULT: %v", r)
  81. }
  82. }()
  83. data := utils.FormatTransactionData(dataStr)
  84. var err error
  85. script := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) {
  86. ret, err = ethutil.Compile(s, false)
  87. return
  88. })
  89. if err != nil {
  90. self.Logln(err)
  91. return
  92. }
  93. var (
  94. gas = ethutil.Big(gasStr)
  95. gasPrice = ethutil.Big(gasPriceStr)
  96. value = ethutil.Big(valueStr)
  97. // Contract addr as test address
  98. keyPair = self.lib.eth.KeyManager().KeyPair()
  99. )
  100. state := self.lib.eth.StateManager().TransState()
  101. account := self.lib.eth.StateManager().TransState().GetAccount(keyPair.Address())
  102. contract := ethstate.NewStateObject([]byte{0})
  103. contract.Balance = value
  104. self.SetAsm(script)
  105. block := self.lib.eth.BlockChain().CurrentBlock
  106. callerClosure := ethvm.NewClosure(&ethstate.Message{}, account, contract, script, gas, gasPrice)
  107. env := utils.NewEnv(state, block, account.Address(), value)
  108. vm := ethvm.New(env)
  109. vm.Verbose = true
  110. vm.Dbg = self.Db
  111. self.vm = vm
  112. self.Db.done = false
  113. self.Logf("callsize %d", len(script))
  114. go func() {
  115. ret, g, err := callerClosure.Call(vm, data)
  116. tot := new(big.Int).Mul(g, gasPrice)
  117. self.Logf("gas usage %v total price = %v (%v)", g, tot, ethutil.CurrencyToString(tot))
  118. if err != nil {
  119. self.Logln("exited with errors:", err)
  120. } else {
  121. if len(ret) > 0 {
  122. self.Logf("exited: % x", ret)
  123. } else {
  124. self.Logf("exited: nil")
  125. }
  126. }
  127. state.Reset()
  128. if !self.Db.interrupt {
  129. self.Db.done = true
  130. } else {
  131. self.Db.interrupt = false
  132. }
  133. }()
  134. }
  135. func (self *DebuggerWindow) Logf(format string, v ...interface{}) {
  136. self.win.Root().Call("setLog", fmt.Sprintf(format, v...))
  137. }
  138. func (self *DebuggerWindow) Logln(v ...interface{}) {
  139. str := fmt.Sprintln(v...)
  140. self.Logf("%s", str[:len(str)-1])
  141. }
  142. func (self *DebuggerWindow) Next() {
  143. self.Db.Next()
  144. }
  145. func (self *DebuggerWindow) Continue() {
  146. self.vm.Stepping = false
  147. self.Next()
  148. }
  149. func (self *DebuggerWindow) ExecCommand(command string) {
  150. if len(command) > 0 {
  151. cmd := strings.Split(command, " ")
  152. switch cmd[0] {
  153. case "help":
  154. self.Logln("Debugger commands:")
  155. self.Logln("break, bp Set breakpoint on instruction")
  156. self.Logln("clear [log, break, bp] Clears previous set sub-command(s)")
  157. case "break", "bp":
  158. if len(cmd) > 1 {
  159. lineNo, err := strconv.Atoi(cmd[1])
  160. if err != nil {
  161. self.Logln(err)
  162. break
  163. }
  164. self.Db.breakPoints = append(self.Db.breakPoints, int64(lineNo))
  165. self.Logf("break point set on instruction %d", lineNo)
  166. } else {
  167. self.Logf("'%s' requires line number", cmd[0])
  168. }
  169. case "clear":
  170. if len(cmd) > 1 {
  171. switch cmd[1] {
  172. case "break", "bp":
  173. self.Db.breakPoints = nil
  174. self.Logln("Breakpoints cleared")
  175. case "log":
  176. self.ClearLog()
  177. default:
  178. self.Logf("clear '%s' is not valid", cmd[1])
  179. }
  180. } else {
  181. self.Logln("'clear' requires sub command")
  182. }
  183. default:
  184. self.Logf("Unknown command %s", cmd[0])
  185. }
  186. }
  187. }
  188. type Debugger struct {
  189. N chan bool
  190. Q chan bool
  191. done, interrupt bool
  192. breakPoints []int64
  193. main *DebuggerWindow
  194. win *qml.Window
  195. }
  196. func NewDebugger(main *DebuggerWindow) *Debugger {
  197. db := &Debugger{make(chan bool), make(chan bool), true, false, nil, main, main.win}
  198. return db
  199. }
  200. type storeVal struct {
  201. Key, Value string
  202. }
  203. func (self *Debugger) BreakHook(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *ethvm.Stack, stateObject *ethstate.StateObject) bool {
  204. self.main.Logln("break on instr:", pc)
  205. return self.halting(pc, op, mem, stack, stateObject)
  206. }
  207. func (self *Debugger) StepHook(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *ethvm.Stack, stateObject *ethstate.StateObject) bool {
  208. return self.halting(pc, op, mem, stack, stateObject)
  209. }
  210. func (self *Debugger) SetCode(byteCode []byte) {
  211. self.main.SetAsm(byteCode)
  212. }
  213. func (self *Debugger) BreakPoints() []int64 {
  214. return self.breakPoints
  215. }
  216. func (d *Debugger) halting(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *ethvm.Stack, stateObject *ethstate.StateObject) bool {
  217. d.win.Root().Call("setInstruction", pc)
  218. d.win.Root().Call("clearMem")
  219. d.win.Root().Call("clearStack")
  220. d.win.Root().Call("clearStorage")
  221. addr := 0
  222. for i := 0; i+32 <= mem.Len(); i += 32 {
  223. d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("% x", mem.Data()[i:i+32])})
  224. addr++
  225. }
  226. for _, val := range stack.Data() {
  227. d.win.Root().Call("setStack", val.String())
  228. }
  229. stateObject.EachStorage(func(key string, node *ethutil.Value) {
  230. d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", key), fmt.Sprintf("% x", node.Str())})
  231. })
  232. d.win.Root().ObjectByName("info").Set("text", fmt.Sprintf(`stack frame %v`, new(big.Int).SetBytes(mem.Get(0, 32))))
  233. out:
  234. for {
  235. select {
  236. case <-d.N:
  237. break out
  238. case <-d.Q:
  239. d.interrupt = true
  240. d.clearBuffers()
  241. return false
  242. }
  243. }
  244. return true
  245. }
  246. func (d *Debugger) clearBuffers() {
  247. out:
  248. // drain
  249. for {
  250. select {
  251. case <-d.N:
  252. case <-d.Q:
  253. default:
  254. break out
  255. }
  256. }
  257. }
  258. func (d *Debugger) Next() {
  259. if !d.done {
  260. d.N <- true
  261. }
  262. }