-
Notifications
You must be signed in to change notification settings - Fork 493
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
TealDbg: Support for StepOver and refactoring object IDs #3653
Conversation
Codecov Report
@@ 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
Continue to review full report at Codecov.
|
There was a problem hiding this 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
cmd/tealdbg/debugger.go
Outdated
defer s.mu.Unlock() | ||
// Step over a function call (callsub op). | ||
if currentOp == "callsub" { | ||
err := s.setBreakpoint(currentLine + 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
log the error?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
NoBreak bool `json:"nobreak"` | ||
StepBreak bool `json:"stepbreak"` | ||
StepOutOver bool `json:"stepover"` |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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{}{} |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
…_split_test_cases Refactor TestCallStackControl into subtests
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.
|
There was a problem hiding this 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. ☕
There was a problem hiding this 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. |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
)
Summary
This PR contains:
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).RunAll
in tealdbg to ensure proper group txn execution.This should also close #2289
Test Plan
local_test.go
anddebugger_test.go
./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
.