Skip to content

Commit

Permalink
TealDbg: Support for StepOver and refactoring object IDs (#3653)
Browse files Browse the repository at this point in the history
  • Loading branch information
algochoi authored Apr 13, 2022
1 parent 85a02f7 commit b0c551a
Show file tree
Hide file tree
Showing 11 changed files with 862 additions and 121 deletions.
67 changes: 50 additions & 17 deletions cmd/tealdbg/cdtSession.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ func (s *cdtSession) websocketHandler(w http.ResponseWriter, r *http.Request) {
// set pc and line to 0 to workaround Register ack
state.Update(cdtStateUpdate{
dbgState.Stack, dbgState.Scratch,
0, 0, "",
dbgState.OpcodeBudget, s.debugger.GetStates(nil),
0, 0, "", dbgState.OpcodeBudget, dbgState.CallStack,
s.debugger.GetStates(nil),
})

hash := sha256.Sum256([]byte(state.disassembly)) // some random hash
Expand Down Expand Up @@ -247,7 +247,7 @@ func (s *cdtSession) websocketHandler(w http.ResponseWriter, r *http.Request) {
state.Update(cdtStateUpdate{
dbgState.Stack, dbgState.Scratch,
dbgState.PC, dbgState.Line, dbgState.Error,
dbgState.OpcodeBudget, appState,
dbgState.OpcodeBudget, dbgState.CallStack, appState,
})
dbgStateMu.Unlock()

Expand Down Expand Up @@ -473,14 +473,26 @@ func (s *cdtSession) handleCdtRequest(req *cdt.ChromeRequest, state *cdtState) (
response = cdt.ChromeResponse{ID: req.ID, Result: empty}
case "Debugger.stepOut":
state.lastAction.Store("step")
state.pauseOnCompeted.SetTo(true)
s.debugger.Resume()
if len(state.callStack) == 0 {
// If we are not in a subroutine, pause at the end so user can
// inspect the final state of the program.
state.pauseOnCompleted.SetTo(true)
}
s.debugger.StepOut()
if state.completed.IsSet() {
evDestroyed := s.makeContextDestroyedEvent()
events = append(events, &evDestroyed)
}
response = cdt.ChromeResponse{ID: req.ID, Result: empty}
case "Debugger.stepOver":
state.lastAction.Store("step")
s.debugger.StepOver()
if state.completed.IsSet() {
evDestroyed := s.makeContextDestroyedEvent()
events = append(events, &evDestroyed)
}
response = cdt.ChromeResponse{ID: req.ID, Result: empty}
case "Debugger.stepOver", "Debugger.stepInto":
case "Debugger.stepInto":
state.lastAction.Store("step")
s.debugger.Step()
if state.completed.IsSet() {
Expand All @@ -497,7 +509,7 @@ func (s *cdtSession) handleCdtRequest(req *cdt.ChromeRequest, state *cdtState) (

func (s *cdtSession) computeEvent(state *cdtState) (event interface{}) {
if state.completed.IsSet() {
if state.pauseOnCompeted.IsSet() {
if state.pauseOnCompleted.IsSet() {
event = s.makeDebuggerPausedEvent(state)
return
}
Expand Down Expand Up @@ -571,22 +583,43 @@ func (s *cdtSession) makeDebuggerPausedEvent(state *cdtState) cdt.DebuggerPaused
},
}
sc := []cdt.DebuggerScope{scopeLocal, scopeGlobal}
cf := cdt.DebuggerCallFrame{
CallFrameID: "mainframe",
FunctionName: "",
Location: &cdt.DebuggerLocation{
ScriptID: s.scriptID,
LineNumber: state.line.Load(),
ColumnNumber: 0,

cfs := []cdt.DebuggerCallFrame{
{
CallFrameID: "mainframe",
FunctionName: "main",
Location: &cdt.DebuggerLocation{
ScriptID: s.scriptID,
LineNumber: state.line.Load(),
ColumnNumber: 0,
},
URL: s.scriptURL,
ScopeChain: sc,
},
URL: s.scriptURL,
ScopeChain: sc,
}
for i := range state.callStack {
cf := cdt.DebuggerCallFrame{
CallFrameID: "mainframe",
FunctionName: state.callStack[i].LabelName,
Location: &cdt.DebuggerLocation{
ScriptID: s.scriptID,
LineNumber: state.line.Load(),
ColumnNumber: 0,
},
URL: s.scriptURL,
ScopeChain: sc,
}
// Set the previous call frame line number
cfs[0].Location.LineNumber = state.callStack[i].FrameLine
// We have to prepend the newest frame for it to appear first
// in the debugger...
cfs = append([]cdt.DebuggerCallFrame{cf}, cfs...)
}

evPaused := cdt.DebuggerPausedEvent{
Method: "Debugger.paused",
Params: cdt.DebuggerPausedParams{
CallFrames: []cdt.DebuggerCallFrame{cf},
CallFrames: cfs,
Reason: "other",
HitBreakpoints: make([]string, 0),
},
Expand Down
2 changes: 1 addition & 1 deletion cmd/tealdbg/cdtSession_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ func TestCdtSessionStateToEvent(t *testing.T) {

// if completed and pause on competed then pause
state.completed.SetTo(true)
state.pauseOnCompeted.SetTo(true)
state.pauseOnCompleted.SetTo(true)
e = s.computeEvent(&state)
_, ok = (e).(cdt.DebuggerPausedEvent)
require.True(t, ok)
Expand Down
68 changes: 13 additions & 55 deletions cmd/tealdbg/cdtState.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,20 @@ type cdtState struct {
globals []basics.TealValue

// mutable program state
mu deadlock.Mutex
stack []basics.TealValue
scratch []basics.TealValue
pc atomicInt
line atomicInt
err atomicString
mu deadlock.Mutex
stack []basics.TealValue
scratch []basics.TealValue
pc atomicInt
line atomicInt
err atomicString
callStack []logic.CallFrame
AppState

// debugger states
lastAction atomicString
pauseOnError atomicBool
pauseOnCompeted atomicBool
completed atomicBool
lastAction atomicString
pauseOnError atomicBool
pauseOnCompleted atomicBool
completed atomicBool
}

type cdtStateUpdate struct {
Expand All @@ -64,6 +65,7 @@ type cdtStateUpdate struct {
line int
err string
opcodeBudget int
callStack []logic.CallFrame

AppState
}
Expand Down Expand Up @@ -110,37 +112,7 @@ func (s *cdtState) Update(state cdtStateUpdate) {
s.AppState = state.AppState
// We need to dynamically override opcodeBudget with the proper value each step.
s.globals[logic.OpcodeBudget].Uint = uint64(state.opcodeBudget)
}

const localScopeObjID = "localScopeObjId"
const globalScopeObjID = "globalScopeObjID"
const globalsObjID = "globalsObjID"
const txnObjID = "txnObjID"
const gtxnObjID = "gtxnObjID"
const stackObjID = "stackObjID"
const scratchObjID = "scratchObjID"
const tealErrorID = "tealErrorID"
const appGlobalObjID = "appGlobalObjID"
const appLocalsObjID = "appLocalsObjID"
const txnArrayFieldObjID = "txnArrayField"
const logsObjID = "logsObjID"
const innerTxnsObjID = "innerTxnsObjID"

type objectDescFn func(s *cdtState, preview bool) []cdt.RuntimePropertyDescriptor

var objectDescMap = map[string]objectDescFn{
globalScopeObjID: makeGlobalScope,
localScopeObjID: makeLocalScope,
globalsObjID: makeGlobals,
txnObjID: makeTxn,
gtxnObjID: makeTxnGroup,
stackObjID: makeStack,
scratchObjID: makeScratch,
tealErrorID: makeTealError,
appGlobalObjID: makeAppGlobalState,
appLocalsObjID: makeAppLocalsState,
logsObjID: makeLogsState,
innerTxnsObjID: makeInnerTxnsState,
s.callStack = state.callStack
}

func (s *cdtState) getObjectDescriptor(objID string, preview bool) (desc []cdt.RuntimePropertyDescriptor, err error) {
Expand Down Expand Up @@ -591,8 +563,6 @@ func makeGlobalsPreview(globals []basics.TealValue) cdt.RuntimeObjectPreview {
return p
}

var gtxnObjIDPrefix = fmt.Sprintf("%s_gid_", gtxnObjID)

func encodeGroupTxnID(groupIndex int) string {
return gtxnObjIDPrefix + strconv.Itoa(groupIndex)
}
Expand All @@ -606,10 +576,6 @@ func decodeGroupTxnID(objID string) (int, bool) {
return 0, false
}

var logObjIDPrefix = fmt.Sprintf("%s_id", logsObjID)
var innerTxnObjIDPrefix = fmt.Sprintf("%s_id", innerTxnsObjID)
var innerNestedTxnObjIDPrefix = fmt.Sprintf("%s_nested", innerTxnsObjID)

func encodeNestedObjID(groupIndexes []int, prefix string) string {
encodedElements := []string{prefix}
for _, i := range groupIndexes {
Expand Down Expand Up @@ -695,8 +661,6 @@ func decodeArraySlice(objID string) (string, int, int, bool) {
return "", 0, 0, false
}

var appGlobalObjIDPrefix = fmt.Sprintf("%s_", appGlobalObjID)

func encodeAppGlobalAppID(key string) string {
return appGlobalObjIDPrefix + key
}
Expand All @@ -710,8 +674,6 @@ func decodeAppGlobalAppID(objID string) (uint64, bool) {
return 0, false
}

var appLocalsObjIDPrefix = fmt.Sprintf("%s_", appLocalsObjID)

func encodeAppLocalsAddr(addr string) string {
return appLocalsObjIDPrefix + addr
}
Expand All @@ -723,8 +685,6 @@ func decodeAppLocalsAddr(objID string) (string, bool) {
return "", false
}

var appLocalAppIDPrefix = fmt.Sprintf("%s__", appLocalsObjID)

func encodeAppLocalsAppID(addr string, appID string) string {
return fmt.Sprintf("%s%s_%s", appLocalAppIDPrefix, addr, appID)
}
Expand All @@ -740,8 +700,6 @@ func decodeAppLocalsAppID(objID string) (string, uint64, bool) {
return "", 0, false
}

var txnArrayFieldPrefix = fmt.Sprintf("%s__", txnArrayFieldObjID)

func encodeTxnArrayField(groupIndex int, field int) string {
return fmt.Sprintf("%s%d_%d", txnArrayFieldPrefix, groupIndex, field)
}
Expand Down
67 changes: 67 additions & 0 deletions cmd/tealdbg/cdtStateObjects.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (C) 2019-2022 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.

package main

import (
"github.com/algorand/go-algorand/cmd/tealdbg/cdt"
)

// Object IDs
const (
localScopeObjID = "localScopeObjId"
globalScopeObjID = "globalScopeObjID"
globalsObjID = "globalsObjID"
txnObjID = "txnObjID"
gtxnObjID = "gtxnObjID"
stackObjID = "stackObjID"
scratchObjID = "scratchObjID"
tealErrorID = "tealErrorID"
appGlobalObjID = "appGlobalObjID"
appLocalsObjID = "appLocalsObjID"
txnArrayFieldObjID = "txnArrayField"
logsObjID = "logsObjID"
innerTxnsObjID = "innerTxnsObjID"
)

// Object Prefix IDs
const (
gtxnObjIDPrefix = gtxnObjID + "_gid_"
logObjIDPrefix = logsObjID + "_id"
innerTxnObjIDPrefix = innerTxnsObjID + "_id"
innerNestedTxnObjIDPrefix = innerTxnsObjID + "_nested"
appGlobalObjIDPrefix = appGlobalObjID + "_"
appLocalsObjIDPrefix = appLocalsObjID + "_"
appLocalAppIDPrefix = appLocalsObjID + "__"
txnArrayFieldPrefix = txnArrayFieldObjID + "__"
)

type objectDescFn func(s *cdtState, preview bool) []cdt.RuntimePropertyDescriptor

var objectDescMap = map[string]objectDescFn{
globalScopeObjID: makeGlobalScope,
localScopeObjID: makeLocalScope,
globalsObjID: makeGlobals,
txnObjID: makeTxn,
gtxnObjID: makeTxnGroup,
stackObjID: makeStack,
scratchObjID: makeScratch,
tealErrorID: makeTealError,
appGlobalObjID: makeAppGlobalState,
appLocalsObjID: makeAppLocalsState,
logsObjID: makeLogsState,
innerTxnsObjID: makeInnerTxnsState,
}
6 changes: 6 additions & 0 deletions cmd/tealdbg/cdtdbg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ type MockDebugControl struct {
func (c *MockDebugControl) Step() {
}

func (c *MockDebugControl) StepOver() {
}

func (c *MockDebugControl) StepOut() {
}

func (c *MockDebugControl) Resume() {
}

Expand Down
Loading

0 comments on commit b0c551a

Please sign in to comment.