Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TealDbg: Support for StepOver and refactoring object IDs #3653

Merged
merged 36 commits into from
Apr 13, 2022

Conversation

algochoi
Copy link
Contributor

@algochoi algochoi commented Feb 17, 2022

Summary

This PR contains:

  • Support for stepping over subroutines when there is a callsub op. Dynamically inspects the callstack to make sure depth is equal to what it entered as. Also added a channel so that the updates can be synchronized within the debugger control (else updates may be received at any time and the state may not be updated before the depth check happens).
  • Refactors object IDs in a separate file. We may want to map out the actual addresses to objects in the future.
  • Adds a unit test for RunAll in tealdbg to ensure proper group txn execution.
  • Include and expose the call stack in CDT.

This should also close #2289

Test Plan

  • Unit tests in local_test.go and debugger_test.go
  • Ran tealdbg on contracts with subroutines. Example dryrun here, and can be run with ./tealdbg debug -d path-to-file/generated-data/dryrun_subroutine.msgp

gload seems to work in tealdbg if the entire txn group is supplied. e.g. through dryrun: tealdbg debug -d dryrun_grp.msgp.

@codecov-commenter
Copy link

codecov-commenter commented Feb 17, 2022

Codecov Report

Merging #3653 (f42f312) into master (32cfc42) will decrease coverage by 0.00%.
The diff coverage is 71.79%.

@@            Coverage Diff             @@
##           master    #3653      +/-   ##
==========================================
- Coverage   49.99%   49.99%   -0.01%     
==========================================
  Files         392      392              
  Lines       68495    68579      +84     
==========================================
+ Hits        34242    34283      +41     
- Misses      30497    30537      +40     
- Partials     3756     3759       +3     
Impacted Files Coverage Δ
cmd/tealdbg/cdtState.go 84.47% <0.00%> (-0.15%) ⬇️
cmd/tealdbg/webdbg.go 57.79% <0.00%> (-4.71%) ⬇️
cmd/tealdbg/cdtSession.go 66.74% <57.57%> (-1.14%) ⬇️
cmd/tealdbg/debugger.go 73.60% <86.88%> (+2.17%) ⬆️
data/transactions/logic/debugger.go 61.46% <100.00%> (+4.76%) ⬆️
ledger/blockqueue.go 82.18% <0.00%> (-2.88%) ⬇️
network/wsPeer.go 65.83% <0.00%> (-2.78%) ⬇️
data/abi/abi_type.go 87.67% <0.00%> (-0.95%) ⬇️
ledger/tracker.go 70.81% <0.00%> (-0.86%) ⬇️
ledger/acctupdates.go 68.51% <0.00%> (-0.66%) ⬇️
... and 4 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 32cfc42...f42f312. Read the comment docs.

@algochoi algochoi changed the title TealDbg support for StepOver and refactoring object IDs TealDbg: Support for StepOver and refactoring object IDs Feb 23, 2022
@algochoi algochoi marked this pull request as ready for review March 10, 2022 21:29
Copy link
Contributor

@algorandskiy algorandskiy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, couple comments

defer s.mu.Unlock()
// Step over a function call (callsub op).
if currentOp == "callsub" {
err := s.setBreakpoint(currentLine + 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log the error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the only time it would error would be if callsub was at the end of the program and setBreakpoint() will try to set a breakpoint past the program. In that case, it feels normal to not log something and just continue, but maybe I'm missing a case.

lines := strings.Split(d.Disassembly, "\n")
for _, pc := range callstack {
// The callsub is pc - 3 from the callstack pc
callsubLineNum := d.PCToLine(pc - 3)
Copy link
Contributor Author

@algochoi algochoi Mar 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a bit of hardcoding here (callsub saves pc+3 to the callstack in eval.go) and added a "soft" check to see if the line is a callsub later on so we can try to parse the label to make the Call Stack look nicer in CDT.

Comment on lines +96 to +98
NoBreak bool `json:"nobreak"`
StepBreak bool `json:"stepbreak"`
StepOutOver bool `json:"stepover"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: It looks like not all the combinations are valid (for example NoBreak=true and StepBreak=true). This is an indication these might be flags and not boolean variables

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@algorandskiy I have a similar feeling though I'm wondering if you feel strongly enough that we ought to hold the PR or if we can accept as is and revisit in a future iteration?

From my side - Given the ground already covered here, I feel like it'd be OK to accept as is.

// setActiveBreak does not check if the line is a valid value, so it should
// be called inside the setBreakpoint() in session.
func (dc *debugConfig) setActiveBreak(line int) {
dc.ActiveBreak[line] = struct{}{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Andrew, I might have already asked but can't find the answer - what is the point of having ActiveBreak map? It is only set in setActiveBreak. And everywhere we call setBreackpoint the contol state is reset s.debugConfig = makeDebugConfig() leaving only a single entry in ActiveBreak.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's only used in the Resume() to set multiple active breakpoints (in case we jump backwards), but you're right that in everywhere else there is only 1 active break.

@jasonpaulos
Copy link
Contributor

I've manually tested on the following recursive TEAL program, and step in and step over are working great! My only complaint is that step out is frequently buggy and takes me to the end of the program, though I've not yet debugged why this happens.

#pragma version 6
int 5
callsub factorial
return

factorial:
dup
int 1
<=
bz recursive_step
pop
int 1
retsub
recursive_step:
dup
int 1
-
callsub factorial
*
retsub

michaeldiamant
michaeldiamant previously approved these changes Mar 31, 2022
Copy link
Contributor

@michaeldiamant michaeldiamant left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@algochoi Thanks for your patience working through multiple iterations to enrich tealdbg. ☕

michaeldiamant
michaeldiamant previously approved these changes Apr 1, 2022
Copy link
Contributor

@michaeldiamant michaeldiamant left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approving after minor change dismissed my prior approval. Still looks good to me.

return nil
}

func (s *session) SetBreakpoint(line int) error {
s.mu.Lock()
defer s.mu.Unlock()
// Reset all existing flags and breakpoints and set a new bp.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why moved the resetting s.debugConfig from SetBreakpoint to setBreakpoint?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch - I will move this back into setBreakpoint

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, for Resume there can be multiple active breakpoints (code execution can jump backward and break on a previous bp) set so I moved this outside instead - let me know if that makes sense.

@@ -174,3 +174,27 @@ func TestValueDeltaToValueDelta(t *testing.T) {
require.Equal(t, base64.StdEncoding.EncodeToString([]byte(vDelta.Bytes)), ans.Bytes)
require.Equal(t, vDelta.Uint, ans.Uint)
}

func TestParseCallstack(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a real test for with two nested callstack received from the evaluator via debugger.Update.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point - added unit test below (real test checks line+1 due to pragma)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update tealdbg for TEAL 4
5 participants