Skip to content

Commit b0c551a

Browse files
authored
TealDbg: Support for StepOver and refactoring object IDs (#3653)
1 parent 85a02f7 commit b0c551a

File tree

11 files changed

+862
-121
lines changed

11 files changed

+862
-121
lines changed

cmd/tealdbg/cdtSession.go

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,8 @@ func (s *cdtSession) websocketHandler(w http.ResponseWriter, r *http.Request) {
168168
// set pc and line to 0 to workaround Register ack
169169
state.Update(cdtStateUpdate{
170170
dbgState.Stack, dbgState.Scratch,
171-
0, 0, "",
172-
dbgState.OpcodeBudget, s.debugger.GetStates(nil),
171+
0, 0, "", dbgState.OpcodeBudget, dbgState.CallStack,
172+
s.debugger.GetStates(nil),
173173
})
174174

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

@@ -473,14 +473,26 @@ func (s *cdtSession) handleCdtRequest(req *cdt.ChromeRequest, state *cdtState) (
473473
response = cdt.ChromeResponse{ID: req.ID, Result: empty}
474474
case "Debugger.stepOut":
475475
state.lastAction.Store("step")
476-
state.pauseOnCompeted.SetTo(true)
477-
s.debugger.Resume()
476+
if len(state.callStack) == 0 {
477+
// If we are not in a subroutine, pause at the end so user can
478+
// inspect the final state of the program.
479+
state.pauseOnCompleted.SetTo(true)
480+
}
481+
s.debugger.StepOut()
482+
if state.completed.IsSet() {
483+
evDestroyed := s.makeContextDestroyedEvent()
484+
events = append(events, &evDestroyed)
485+
}
486+
response = cdt.ChromeResponse{ID: req.ID, Result: empty}
487+
case "Debugger.stepOver":
488+
state.lastAction.Store("step")
489+
s.debugger.StepOver()
478490
if state.completed.IsSet() {
479491
evDestroyed := s.makeContextDestroyedEvent()
480492
events = append(events, &evDestroyed)
481493
}
482494
response = cdt.ChromeResponse{ID: req.ID, Result: empty}
483-
case "Debugger.stepOver", "Debugger.stepInto":
495+
case "Debugger.stepInto":
484496
state.lastAction.Store("step")
485497
s.debugger.Step()
486498
if state.completed.IsSet() {
@@ -497,7 +509,7 @@ func (s *cdtSession) handleCdtRequest(req *cdt.ChromeRequest, state *cdtState) (
497509

498510
func (s *cdtSession) computeEvent(state *cdtState) (event interface{}) {
499511
if state.completed.IsSet() {
500-
if state.pauseOnCompeted.IsSet() {
512+
if state.pauseOnCompleted.IsSet() {
501513
event = s.makeDebuggerPausedEvent(state)
502514
return
503515
}
@@ -571,22 +583,43 @@ func (s *cdtSession) makeDebuggerPausedEvent(state *cdtState) cdt.DebuggerPaused
571583
},
572584
}
573585
sc := []cdt.DebuggerScope{scopeLocal, scopeGlobal}
574-
cf := cdt.DebuggerCallFrame{
575-
CallFrameID: "mainframe",
576-
FunctionName: "",
577-
Location: &cdt.DebuggerLocation{
578-
ScriptID: s.scriptID,
579-
LineNumber: state.line.Load(),
580-
ColumnNumber: 0,
586+
587+
cfs := []cdt.DebuggerCallFrame{
588+
{
589+
CallFrameID: "mainframe",
590+
FunctionName: "main",
591+
Location: &cdt.DebuggerLocation{
592+
ScriptID: s.scriptID,
593+
LineNumber: state.line.Load(),
594+
ColumnNumber: 0,
595+
},
596+
URL: s.scriptURL,
597+
ScopeChain: sc,
581598
},
582-
URL: s.scriptURL,
583-
ScopeChain: sc,
599+
}
600+
for i := range state.callStack {
601+
cf := cdt.DebuggerCallFrame{
602+
CallFrameID: "mainframe",
603+
FunctionName: state.callStack[i].LabelName,
604+
Location: &cdt.DebuggerLocation{
605+
ScriptID: s.scriptID,
606+
LineNumber: state.line.Load(),
607+
ColumnNumber: 0,
608+
},
609+
URL: s.scriptURL,
610+
ScopeChain: sc,
611+
}
612+
// Set the previous call frame line number
613+
cfs[0].Location.LineNumber = state.callStack[i].FrameLine
614+
// We have to prepend the newest frame for it to appear first
615+
// in the debugger...
616+
cfs = append([]cdt.DebuggerCallFrame{cf}, cfs...)
584617
}
585618

586619
evPaused := cdt.DebuggerPausedEvent{
587620
Method: "Debugger.paused",
588621
Params: cdt.DebuggerPausedParams{
589-
CallFrames: []cdt.DebuggerCallFrame{cf},
622+
CallFrames: cfs,
590623
Reason: "other",
591624
HitBreakpoints: make([]string, 0),
592625
},

cmd/tealdbg/cdtSession_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ func TestCdtSessionStateToEvent(t *testing.T) {
436436

437437
// if completed and pause on competed then pause
438438
state.completed.SetTo(true)
439-
state.pauseOnCompeted.SetTo(true)
439+
state.pauseOnCompleted.SetTo(true)
440440
e = s.computeEvent(&state)
441441
_, ok = (e).(cdt.DebuggerPausedEvent)
442442
require.True(t, ok)

cmd/tealdbg/cdtState.go

Lines changed: 13 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,20 @@ type cdtState struct {
4242
globals []basics.TealValue
4343

4444
// mutable program state
45-
mu deadlock.Mutex
46-
stack []basics.TealValue
47-
scratch []basics.TealValue
48-
pc atomicInt
49-
line atomicInt
50-
err atomicString
45+
mu deadlock.Mutex
46+
stack []basics.TealValue
47+
scratch []basics.TealValue
48+
pc atomicInt
49+
line atomicInt
50+
err atomicString
51+
callStack []logic.CallFrame
5152
AppState
5253

5354
// debugger states
54-
lastAction atomicString
55-
pauseOnError atomicBool
56-
pauseOnCompeted atomicBool
57-
completed atomicBool
55+
lastAction atomicString
56+
pauseOnError atomicBool
57+
pauseOnCompleted atomicBool
58+
completed atomicBool
5859
}
5960

6061
type cdtStateUpdate struct {
@@ -64,6 +65,7 @@ type cdtStateUpdate struct {
6465
line int
6566
err string
6667
opcodeBudget int
68+
callStack []logic.CallFrame
6769

6870
AppState
6971
}
@@ -110,37 +112,7 @@ func (s *cdtState) Update(state cdtStateUpdate) {
110112
s.AppState = state.AppState
111113
// We need to dynamically override opcodeBudget with the proper value each step.
112114
s.globals[logic.OpcodeBudget].Uint = uint64(state.opcodeBudget)
113-
}
114-
115-
const localScopeObjID = "localScopeObjId"
116-
const globalScopeObjID = "globalScopeObjID"
117-
const globalsObjID = "globalsObjID"
118-
const txnObjID = "txnObjID"
119-
const gtxnObjID = "gtxnObjID"
120-
const stackObjID = "stackObjID"
121-
const scratchObjID = "scratchObjID"
122-
const tealErrorID = "tealErrorID"
123-
const appGlobalObjID = "appGlobalObjID"
124-
const appLocalsObjID = "appLocalsObjID"
125-
const txnArrayFieldObjID = "txnArrayField"
126-
const logsObjID = "logsObjID"
127-
const innerTxnsObjID = "innerTxnsObjID"
128-
129-
type objectDescFn func(s *cdtState, preview bool) []cdt.RuntimePropertyDescriptor
130-
131-
var objectDescMap = map[string]objectDescFn{
132-
globalScopeObjID: makeGlobalScope,
133-
localScopeObjID: makeLocalScope,
134-
globalsObjID: makeGlobals,
135-
txnObjID: makeTxn,
136-
gtxnObjID: makeTxnGroup,
137-
stackObjID: makeStack,
138-
scratchObjID: makeScratch,
139-
tealErrorID: makeTealError,
140-
appGlobalObjID: makeAppGlobalState,
141-
appLocalsObjID: makeAppLocalsState,
142-
logsObjID: makeLogsState,
143-
innerTxnsObjID: makeInnerTxnsState,
115+
s.callStack = state.callStack
144116
}
145117

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

594-
var gtxnObjIDPrefix = fmt.Sprintf("%s_gid_", gtxnObjID)
595-
596566
func encodeGroupTxnID(groupIndex int) string {
597567
return gtxnObjIDPrefix + strconv.Itoa(groupIndex)
598568
}
@@ -606,10 +576,6 @@ func decodeGroupTxnID(objID string) (int, bool) {
606576
return 0, false
607577
}
608578

609-
var logObjIDPrefix = fmt.Sprintf("%s_id", logsObjID)
610-
var innerTxnObjIDPrefix = fmt.Sprintf("%s_id", innerTxnsObjID)
611-
var innerNestedTxnObjIDPrefix = fmt.Sprintf("%s_nested", innerTxnsObjID)
612-
613579
func encodeNestedObjID(groupIndexes []int, prefix string) string {
614580
encodedElements := []string{prefix}
615581
for _, i := range groupIndexes {
@@ -695,8 +661,6 @@ func decodeArraySlice(objID string) (string, int, int, bool) {
695661
return "", 0, 0, false
696662
}
697663

698-
var appGlobalObjIDPrefix = fmt.Sprintf("%s_", appGlobalObjID)
699-
700664
func encodeAppGlobalAppID(key string) string {
701665
return appGlobalObjIDPrefix + key
702666
}
@@ -710,8 +674,6 @@ func decodeAppGlobalAppID(objID string) (uint64, bool) {
710674
return 0, false
711675
}
712676

713-
var appLocalsObjIDPrefix = fmt.Sprintf("%s_", appLocalsObjID)
714-
715677
func encodeAppLocalsAddr(addr string) string {
716678
return appLocalsObjIDPrefix + addr
717679
}
@@ -723,8 +685,6 @@ func decodeAppLocalsAddr(objID string) (string, bool) {
723685
return "", false
724686
}
725687

726-
var appLocalAppIDPrefix = fmt.Sprintf("%s__", appLocalsObjID)
727-
728688
func encodeAppLocalsAppID(addr string, appID string) string {
729689
return fmt.Sprintf("%s%s_%s", appLocalAppIDPrefix, addr, appID)
730690
}
@@ -740,8 +700,6 @@ func decodeAppLocalsAppID(objID string) (string, uint64, bool) {
740700
return "", 0, false
741701
}
742702

743-
var txnArrayFieldPrefix = fmt.Sprintf("%s__", txnArrayFieldObjID)
744-
745703
func encodeTxnArrayField(groupIndex int, field int) string {
746704
return fmt.Sprintf("%s%d_%d", txnArrayFieldPrefix, groupIndex, field)
747705
}

cmd/tealdbg/cdtStateObjects.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright (C) 2019-2022 Algorand, Inc.
2+
// This file is part of go-algorand
3+
//
4+
// go-algorand is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as
6+
// published by the Free Software Foundation, either version 3 of the
7+
// License, or (at your option) any later version.
8+
//
9+
// go-algorand 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 Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package main
18+
19+
import (
20+
"github.com/algorand/go-algorand/cmd/tealdbg/cdt"
21+
)
22+
23+
// Object IDs
24+
const (
25+
localScopeObjID = "localScopeObjId"
26+
globalScopeObjID = "globalScopeObjID"
27+
globalsObjID = "globalsObjID"
28+
txnObjID = "txnObjID"
29+
gtxnObjID = "gtxnObjID"
30+
stackObjID = "stackObjID"
31+
scratchObjID = "scratchObjID"
32+
tealErrorID = "tealErrorID"
33+
appGlobalObjID = "appGlobalObjID"
34+
appLocalsObjID = "appLocalsObjID"
35+
txnArrayFieldObjID = "txnArrayField"
36+
logsObjID = "logsObjID"
37+
innerTxnsObjID = "innerTxnsObjID"
38+
)
39+
40+
// Object Prefix IDs
41+
const (
42+
gtxnObjIDPrefix = gtxnObjID + "_gid_"
43+
logObjIDPrefix = logsObjID + "_id"
44+
innerTxnObjIDPrefix = innerTxnsObjID + "_id"
45+
innerNestedTxnObjIDPrefix = innerTxnsObjID + "_nested"
46+
appGlobalObjIDPrefix = appGlobalObjID + "_"
47+
appLocalsObjIDPrefix = appLocalsObjID + "_"
48+
appLocalAppIDPrefix = appLocalsObjID + "__"
49+
txnArrayFieldPrefix = txnArrayFieldObjID + "__"
50+
)
51+
52+
type objectDescFn func(s *cdtState, preview bool) []cdt.RuntimePropertyDescriptor
53+
54+
var objectDescMap = map[string]objectDescFn{
55+
globalScopeObjID: makeGlobalScope,
56+
localScopeObjID: makeLocalScope,
57+
globalsObjID: makeGlobals,
58+
txnObjID: makeTxn,
59+
gtxnObjID: makeTxnGroup,
60+
stackObjID: makeStack,
61+
scratchObjID: makeScratch,
62+
tealErrorID: makeTealError,
63+
appGlobalObjID: makeAppGlobalState,
64+
appLocalsObjID: makeAppLocalsState,
65+
logsObjID: makeLogsState,
66+
innerTxnsObjID: makeInnerTxnsState,
67+
}

cmd/tealdbg/cdtdbg_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ type MockDebugControl struct {
106106
func (c *MockDebugControl) Step() {
107107
}
108108

109+
func (c *MockDebugControl) StepOver() {
110+
}
111+
112+
func (c *MockDebugControl) StepOut() {
113+
}
114+
109115
func (c *MockDebugControl) Resume() {
110116
}
111117

0 commit comments

Comments
 (0)