Skip to content

Commit 19258b1

Browse files
authored
Merge pull request #3691 from jsternberg/dap-entrypoint-breakpoint
dap: fix skipped breakpoint when the breakpoint and the entrypoint were the same
2 parents 88b75da + 724afbb commit 19258b1

2 files changed

Lines changed: 88 additions & 20 deletions

File tree

dap/thread.go

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,20 @@ func (t *thread) Evaluate(ctx Context, c gateway.Client, headRef gateway.Referen
7171
}
7272
defer t.reset()
7373

74+
var next *step
7475
action := stepContinue
7576
if cfg.StopOnEntry {
76-
action = stepNext
77+
// If we are stopping on entry, automatically advance to the
78+
// entrypoint.
79+
action, next = stepNext, t.entrypoint
7780
}
7881

7982
var (
8083
k string
8184
refs map[string]gateway.Reference
82-
next = t.entrypoint
8385
err error
8486
)
85-
for next != nil {
87+
for {
8688
event := t.needsDebug(next, action, err)
8789
if event.Reason != "" {
8890
select {
@@ -98,7 +100,9 @@ func (t *thread) Evaluate(ctx Context, c gateway.Client, headRef gateway.Referen
98100
}
99101

100102
t.setBreakpoints(ctx)
101-
k, next, refs, err = t.seekNext(ctx, next, action)
103+
if k, next, refs, err = t.seekNext(ctx, next, action); next == nil {
104+
break
105+
}
102106
}
103107
return nil
104108
}
@@ -509,19 +513,24 @@ func (t *thread) setBreakpoints(ctx Context) {
509513
}
510514

511515
func (t *thread) seekNext(ctx Context, from *step, action stepType) (string, *step, map[string]gateway.Reference, error) {
512-
// If we're at the end, return no digest to signal that
513-
// we should conclude debugging.
514-
var target *step
516+
// Determine how we are going to limit the scan for the next step.
517+
var limit func(s *step) *step
515518
switch action {
516519
case stepNext:
517-
target = t.continueDigest(from, from.next)
520+
limit = func(s *step) *step {
521+
return s.next
522+
}
518523
case stepIn:
519-
target = from.in
524+
limit = func(s *step) *step {
525+
return s.in
526+
}
520527
case stepOut:
521-
target = t.continueDigest(from, from.out)
522-
case stepContinue:
523-
target = t.continueDigest(from, nil)
528+
limit = func(s *step) *step {
529+
return s.out
530+
}
524531
}
532+
533+
target := t.continueDigest(from, limit)
525534
return t.seek(ctx, target)
526535
}
527536

@@ -547,8 +556,10 @@ func (t *thread) seek(ctx Context, target *step) (k string, result *step, mounts
547556
return k, result, refs, nil
548557
}
549558

550-
func (t *thread) continueDigest(from, until *step) *step {
551-
if len(t.bps) == 0 && until == nil {
559+
func (t *thread) continueDigest(from *step, limit func(*step) *step) *step {
560+
// First chance to exit early. If there's no function for limiting
561+
// the until step and no breakpoints then just go directly to the end step.
562+
if len(t.bps) == 0 && limit == nil {
552563
return nil
553564
}
554565

@@ -561,6 +572,27 @@ func (t *thread) continueDigest(from, until *step) *step {
561572
return ok
562573
}
563574

575+
// Special case. When we aren't coming from any step we consider
576+
// whether the entrypoint itself is a breakpoint. If it is, we stop
577+
// there. Otherwise, we treat the entrypoint as the from location.
578+
if from == nil {
579+
if isBreakpoint(t.entrypoint.dgst) {
580+
return t.entrypoint
581+
}
582+
from = t.entrypoint
583+
}
584+
585+
var until *step
586+
if limit != nil {
587+
until = limit(from)
588+
}
589+
590+
// Second chance to exit early. If we've fully resolved from and the
591+
// limit function doesn't return an end step, just go directly to the end.
592+
if len(t.bps) == 0 && until == nil {
593+
return nil
594+
}
595+
564596
next := func(s *step) *step {
565597
cur := s.in
566598
for cur != nil && cur != until {

tests/dap_build.go

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ var dapBuildTests = []func(t *testing.T, sb integration.Sandbox){
7878
testDapBuild,
7979
testDapBuildStopOnEntry,
8080
testDapBuildSetBreakpoints,
81+
testDapBuildEntryBreakpoint,
8182
testDapBuildVerifiedBreakpoints,
8283
testDapBuildLoadedSource,
8384
testDapBuildStepIn,
@@ -121,17 +122,19 @@ func testDapBuildStopOnEntry(t *testing.T, sb integration.Sandbox) {
121122
})
122123

123124
stopped := waitForInterrupt[*dap.StoppedEvent](t, interruptCh)
125+
require.Equal(t, "step", stopped.Body.Reason)
126+
124127
threads := doThreads(t, client)
125128
require.ElementsMatch(t, []int{stopped.Body.ThreadId}, threads)
126129

127-
stackTraceResp := <-daptest.DoRequest[*dap.StackTraceResponse](t, client, &dap.StackTraceRequest{
128-
Request: dap.Request{Command: "stackTrace"},
129-
Arguments: dap.StackTraceArguments{
130-
ThreadId: stopped.Body.ThreadId,
130+
stackFrames := doStackTrace(t, client, stopped.Body.ThreadId)
131+
assertStackTrace(t, stackFrames, []stackFrameMatcher{
132+
{
133+
SourceName: "Dockerfile",
134+
Line: 7,
135+
Name: `^\[stage-1 .*\] COPY`,
131136
},
132137
})
133-
require.True(t, stackTraceResp.Success)
134-
require.Len(t, stackTraceResp.Body.StackFrames, 1)
135138

136139
var exitErr *exec.ExitError
137140
require.ErrorAs(t, done(true), &exitErr)
@@ -202,6 +205,39 @@ func testDapBuildSetBreakpoints(t *testing.T, sb integration.Sandbox) {
202205
require.NoError(t, done(false))
203206
}
204207

208+
// testDapBuildEntryBreakpoint checks that the entrypoint is a valid breakpoint.
209+
func testDapBuildEntryBreakpoint(t *testing.T, sb integration.Sandbox) {
210+
dir := createTestProject(t)
211+
client, done, err := dapBuildCmd(t, sb, withArgs(dir))
212+
require.NoError(t, err)
213+
214+
interruptCh := pollInterruptEvents(client)
215+
doLaunch(t, client, commands.LaunchConfig{
216+
Dockerfile: path.Join(dir, "Dockerfile"),
217+
ContextPath: dir,
218+
},
219+
dap.SourceBreakpoint{Line: 7},
220+
)
221+
222+
stopped := waitForInterrupt[*dap.StoppedEvent](t, interruptCh)
223+
require.Equal(t, "breakpoint", stopped.Body.Reason)
224+
225+
threads := doThreads(t, client)
226+
require.ElementsMatch(t, []int{stopped.Body.ThreadId}, threads)
227+
228+
stackFrames := doStackTrace(t, client, stopped.Body.ThreadId)
229+
assertStackTrace(t, stackFrames, []stackFrameMatcher{
230+
{
231+
SourceName: "Dockerfile",
232+
Line: 7,
233+
Name: `^\[stage-1 .*\] COPY`,
234+
},
235+
})
236+
237+
var exitErr *exec.ExitError
238+
require.ErrorAs(t, done(true), &exitErr)
239+
}
240+
205241
func testDapBuildVerifiedBreakpoints(t *testing.T, sb integration.Sandbox) {
206242
dir := createTestProject(t)
207243
client, done, err := dapBuildCmd(t, sb, withArgs(dir))

0 commit comments

Comments
 (0)