Merge pull request #3691 from jsternberg/dap-entrypoint-breakpoint
dap: fix skipped breakpoint when the breakpoint and the entrypoint were the same
This commit is contained in:
+46
-14
@@ -71,18 +71,20 @@ func (t *thread) Evaluate(ctx Context, c gateway.Client, headRef gateway.Referen
|
|||||||
}
|
}
|
||||||
defer t.reset()
|
defer t.reset()
|
||||||
|
|
||||||
|
var next *step
|
||||||
action := stepContinue
|
action := stepContinue
|
||||||
if cfg.StopOnEntry {
|
if cfg.StopOnEntry {
|
||||||
action = stepNext
|
// If we are stopping on entry, automatically advance to the
|
||||||
|
// entrypoint.
|
||||||
|
action, next = stepNext, t.entrypoint
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
k string
|
k string
|
||||||
refs map[string]gateway.Reference
|
refs map[string]gateway.Reference
|
||||||
next = t.entrypoint
|
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
for next != nil {
|
for {
|
||||||
event := t.needsDebug(next, action, err)
|
event := t.needsDebug(next, action, err)
|
||||||
if event.Reason != "" {
|
if event.Reason != "" {
|
||||||
select {
|
select {
|
||||||
@@ -98,7 +100,9 @@ func (t *thread) Evaluate(ctx Context, c gateway.Client, headRef gateway.Referen
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.setBreakpoints(ctx)
|
t.setBreakpoints(ctx)
|
||||||
k, next, refs, err = t.seekNext(ctx, next, action)
|
if k, next, refs, err = t.seekNext(ctx, next, action); next == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -509,19 +513,24 @@ func (t *thread) setBreakpoints(ctx Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *thread) seekNext(ctx Context, from *step, action stepType) (string, *step, map[string]gateway.Reference, error) {
|
func (t *thread) seekNext(ctx Context, from *step, action stepType) (string, *step, map[string]gateway.Reference, error) {
|
||||||
// If we're at the end, return no digest to signal that
|
// Determine how we are going to limit the scan for the next step.
|
||||||
// we should conclude debugging.
|
var limit func(s *step) *step
|
||||||
var target *step
|
|
||||||
switch action {
|
switch action {
|
||||||
case stepNext:
|
case stepNext:
|
||||||
target = t.continueDigest(from, from.next)
|
limit = func(s *step) *step {
|
||||||
|
return s.next
|
||||||
|
}
|
||||||
case stepIn:
|
case stepIn:
|
||||||
target = from.in
|
limit = func(s *step) *step {
|
||||||
|
return s.in
|
||||||
|
}
|
||||||
case stepOut:
|
case stepOut:
|
||||||
target = t.continueDigest(from, from.out)
|
limit = func(s *step) *step {
|
||||||
case stepContinue:
|
return s.out
|
||||||
target = t.continueDigest(from, nil)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target := t.continueDigest(from, limit)
|
||||||
return t.seek(ctx, target)
|
return t.seek(ctx, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,8 +556,10 @@ func (t *thread) seek(ctx Context, target *step) (k string, result *step, mounts
|
|||||||
return k, result, refs, nil
|
return k, result, refs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thread) continueDigest(from, until *step) *step {
|
func (t *thread) continueDigest(from *step, limit func(*step) *step) *step {
|
||||||
if len(t.bps) == 0 && until == nil {
|
// First chance to exit early. If there's no function for limiting
|
||||||
|
// the until step and no breakpoints then just go directly to the end step.
|
||||||
|
if len(t.bps) == 0 && limit == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,6 +572,27 @@ func (t *thread) continueDigest(from, until *step) *step {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Special case. When we aren't coming from any step we consider
|
||||||
|
// whether the entrypoint itself is a breakpoint. If it is, we stop
|
||||||
|
// there. Otherwise, we treat the entrypoint as the from location.
|
||||||
|
if from == nil {
|
||||||
|
if isBreakpoint(t.entrypoint.dgst) {
|
||||||
|
return t.entrypoint
|
||||||
|
}
|
||||||
|
from = t.entrypoint
|
||||||
|
}
|
||||||
|
|
||||||
|
var until *step
|
||||||
|
if limit != nil {
|
||||||
|
until = limit(from)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second chance to exit early. If we've fully resolved from and the
|
||||||
|
// limit function doesn't return an end step, just go directly to the end.
|
||||||
|
if len(t.bps) == 0 && until == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
next := func(s *step) *step {
|
next := func(s *step) *step {
|
||||||
cur := s.in
|
cur := s.in
|
||||||
for cur != nil && cur != until {
|
for cur != nil && cur != until {
|
||||||
|
|||||||
+42
-6
@@ -78,6 +78,7 @@ var dapBuildTests = []func(t *testing.T, sb integration.Sandbox){
|
|||||||
testDapBuild,
|
testDapBuild,
|
||||||
testDapBuildStopOnEntry,
|
testDapBuildStopOnEntry,
|
||||||
testDapBuildSetBreakpoints,
|
testDapBuildSetBreakpoints,
|
||||||
|
testDapBuildEntryBreakpoint,
|
||||||
testDapBuildVerifiedBreakpoints,
|
testDapBuildVerifiedBreakpoints,
|
||||||
testDapBuildLoadedSource,
|
testDapBuildLoadedSource,
|
||||||
testDapBuildStepIn,
|
testDapBuildStepIn,
|
||||||
@@ -121,17 +122,19 @@ func testDapBuildStopOnEntry(t *testing.T, sb integration.Sandbox) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
stopped := waitForInterrupt[*dap.StoppedEvent](t, interruptCh)
|
stopped := waitForInterrupt[*dap.StoppedEvent](t, interruptCh)
|
||||||
|
require.Equal(t, "step", stopped.Body.Reason)
|
||||||
|
|
||||||
threads := doThreads(t, client)
|
threads := doThreads(t, client)
|
||||||
require.ElementsMatch(t, []int{stopped.Body.ThreadId}, threads)
|
require.ElementsMatch(t, []int{stopped.Body.ThreadId}, threads)
|
||||||
|
|
||||||
stackTraceResp := <-daptest.DoRequest[*dap.StackTraceResponse](t, client, &dap.StackTraceRequest{
|
stackFrames := doStackTrace(t, client, stopped.Body.ThreadId)
|
||||||
Request: dap.Request{Command: "stackTrace"},
|
assertStackTrace(t, stackFrames, []stackFrameMatcher{
|
||||||
Arguments: dap.StackTraceArguments{
|
{
|
||||||
ThreadId: stopped.Body.ThreadId,
|
SourceName: "Dockerfile",
|
||||||
|
Line: 7,
|
||||||
|
Name: `^\[stage-1 .*\] COPY`,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.True(t, stackTraceResp.Success)
|
|
||||||
require.Len(t, stackTraceResp.Body.StackFrames, 1)
|
|
||||||
|
|
||||||
var exitErr *exec.ExitError
|
var exitErr *exec.ExitError
|
||||||
require.ErrorAs(t, done(true), &exitErr)
|
require.ErrorAs(t, done(true), &exitErr)
|
||||||
@@ -202,6 +205,39 @@ func testDapBuildSetBreakpoints(t *testing.T, sb integration.Sandbox) {
|
|||||||
require.NoError(t, done(false))
|
require.NoError(t, done(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testDapBuildEntryBreakpoint checks that the entrypoint is a valid breakpoint.
|
||||||
|
func testDapBuildEntryBreakpoint(t *testing.T, sb integration.Sandbox) {
|
||||||
|
dir := createTestProject(t)
|
||||||
|
client, done, err := dapBuildCmd(t, sb, withArgs(dir))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
interruptCh := pollInterruptEvents(client)
|
||||||
|
doLaunch(t, client, commands.LaunchConfig{
|
||||||
|
Dockerfile: path.Join(dir, "Dockerfile"),
|
||||||
|
ContextPath: dir,
|
||||||
|
},
|
||||||
|
dap.SourceBreakpoint{Line: 7},
|
||||||
|
)
|
||||||
|
|
||||||
|
stopped := waitForInterrupt[*dap.StoppedEvent](t, interruptCh)
|
||||||
|
require.Equal(t, "breakpoint", stopped.Body.Reason)
|
||||||
|
|
||||||
|
threads := doThreads(t, client)
|
||||||
|
require.ElementsMatch(t, []int{stopped.Body.ThreadId}, threads)
|
||||||
|
|
||||||
|
stackFrames := doStackTrace(t, client, stopped.Body.ThreadId)
|
||||||
|
assertStackTrace(t, stackFrames, []stackFrameMatcher{
|
||||||
|
{
|
||||||
|
SourceName: "Dockerfile",
|
||||||
|
Line: 7,
|
||||||
|
Name: `^\[stage-1 .*\] COPY`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
var exitErr *exec.ExitError
|
||||||
|
require.ErrorAs(t, done(true), &exitErr)
|
||||||
|
}
|
||||||
|
|
||||||
func testDapBuildVerifiedBreakpoints(t *testing.T, sb integration.Sandbox) {
|
func testDapBuildVerifiedBreakpoints(t *testing.T, sb integration.Sandbox) {
|
||||||
dir := createTestProject(t)
|
dir := createTestProject(t)
|
||||||
client, done, err := dapBuildCmd(t, sb, withArgs(dir))
|
client, done, err := dapBuildCmd(t, sb, withArgs(dir))
|
||||||
|
|||||||
Reference in New Issue
Block a user