@@ -23,8 +23,9 @@ type thread struct {
2323 name string
2424
2525 // Persistent state from the adapter.
26- idPool * idPool
27- sourceMap * sourceMap
26+ idPool * idPool
27+ sourceMap * sourceMap
28+ breakpointMap * breakpointMap
2829
2930 // Inputs to the evaluate call.
3031 c gateway.Client
@@ -36,6 +37,7 @@ type thread struct {
3637 def * llb.Definition
3738 ops map [digest.Digest ]* pb.Op
3839 head digest.Digest
40+ bps map [digest.Digest ]int
3941
4042 // Runtime state for the evaluate call.
4143 regions []* region
@@ -80,15 +82,18 @@ func (t *thread) Evaluate(ctx Context, c gateway.Client, ref gateway.Reference,
8082 }
8183
8284 for {
85+ if step == stepContinue {
86+ t .setBreakpoints (ctx )
87+ }
8388 pos , err := t .seekNext (ctx , step )
8489
85- reason , desc := t .needsDebug (pos , step , err )
86- if reason == "" {
90+ event := t .needsDebug (pos , step , err )
91+ if event . Reason == "" {
8792 return err
8893 }
8994
9095 select {
91- case step = <- t .pause (ctx , err , reason , desc ):
96+ case step = <- t .pause (ctx , err , event ):
9297 case <- ctx .Done ():
9398 return context .Cause (ctx )
9499 }
@@ -100,7 +105,11 @@ func (t *thread) init(ctx Context, c gateway.Client, ref gateway.Reference, meta
100105 t .ref = ref
101106 t .meta = meta
102107 t .sourcePath = inputs .ContextPath
103- return t .createRegions (ctx )
108+
109+ if err := t .getLLBState (ctx ); err != nil {
110+ return err
111+ }
112+ return t .createRegions ()
104113}
105114
106115func (t * thread ) reset () {
@@ -111,17 +120,23 @@ func (t *thread) reset() {
111120 t .ops = nil
112121}
113122
114- func (t * thread ) needsDebug (target digest.Digest , step stepType , err error ) (reason , desc string ) {
123+ func (t * thread ) needsDebug (target digest.Digest , step stepType , err error ) (e dap. StoppedEventBody ) {
115124 if err != nil {
116- reason = "exception"
117- desc = "Encountered an error during result evaluation"
118- } else if target != "" && step == stepNext {
119- reason = "step"
125+ e .Reason = "exception"
126+ e .Description = "Encountered an error during result evaluation"
127+ } else if step == stepNext && target != "" {
128+ e .Reason = "step"
129+ } else if step == stepContinue {
130+ if id , ok := t .bps [target ]; ok {
131+ e .Reason = "breakpoint"
132+ e .Description = "Paused on breakpoint"
133+ e .HitBreakpointIds = []int {id }
134+ }
120135 }
121136 return
122137}
123138
124- func (t * thread ) pause (c Context , err error , reason , desc string ) <- chan stepType {
139+ func (t * thread ) pause (c Context , err error , event dap. StoppedEventBody ) <- chan stepType {
125140 t .mu .Lock ()
126141 defer t .mu .Unlock ()
127142
@@ -140,13 +155,10 @@ func (t *thread) pause(c Context, err error, reason, desc string) <-chan stepTyp
140155 }
141156 }
142157
158+ event .ThreadId = t .id
143159 c .C () <- & dap.StoppedEvent {
144160 Event : dap.Event {Event : "stopped" },
145- Body : dap.StoppedEventBody {
146- Reason : reason ,
147- Description : desc ,
148- ThreadId : t .id ,
149- },
161+ Body : event ,
150162 }
151163 return t .paused
152164}
@@ -232,6 +244,10 @@ func (t *thread) getLLBState(ctx Context) error {
232244 return err
233245}
234246
247+ func (t * thread ) setBreakpoints (ctx Context ) {
248+ t .bps = t .breakpointMap .Intersect (ctx , t .def .Source , t .sourcePath )
249+ }
250+
235251func (t * thread ) findBacklinks () map [digest.Digest ]map [digest.Digest ]struct {} {
236252 backlinks := make (map [digest.Digest ]map [digest.Digest ]struct {})
237253 for dgst := range t .ops {
@@ -249,11 +265,7 @@ func (t *thread) findBacklinks() map[digest.Digest]map[digest.Digest]struct{} {
249265 return backlinks
250266}
251267
252- func (t * thread ) createRegions (ctx Context ) error {
253- if err := t .getLLBState (ctx ); err != nil {
254- return err
255- }
256-
268+ func (t * thread ) createRegions () error {
257269 // Find the links going from inputs to their outputs.
258270 // This isn't represented in the LLB graph but we need it to ensure
259271 // an op only has one child and whether we are allowed to visit a node.
@@ -363,8 +375,11 @@ func (t *thread) seekNext(ctx Context, step stepType) (digest.Digest, error) {
363375 }
364376
365377 target := t .head
366- if step == stepNext {
367- target = t .nextDigest ()
378+ switch step {
379+ case stepNext :
380+ target = t .nextDigest (nil )
381+ case stepContinue :
382+ target = t .continueDigest ()
368383 }
369384
370385 if target == "" {
@@ -394,11 +409,27 @@ func (t *thread) seek(ctx Context, target digest.Digest) (digest.Digest, error)
394409 return t .curPos , err
395410}
396411
397- func (t * thread ) nextDigest () digest.Digest {
412+ func (t * thread ) nextDigest (fn func (digest.Digest ) bool ) digest.Digest {
413+ isValid := func (dgst digest.Digest ) bool {
414+ // Skip this digest because it has no locations in the source file.
415+ if loc , ok := t .def .Source .Locations [string (dgst )]; ! ok || len (loc .Locations ) == 0 {
416+ return false
417+ }
418+
419+ // If a custom function has been set for validation, use it.
420+ return fn == nil || fn (dgst )
421+ }
422+
398423 // If we have no position, automatically select the first step.
399424 if t .curPos == "" {
400425 r := t .regions [len (t .regions )- 1 ]
401- return r .digests [0 ]
426+ if isValid (r .digests [0 ]) {
427+ return r .digests [0 ]
428+ }
429+
430+ // We cannot use the first position. Treat the first position as our
431+ // current position so we can iterate.
432+ t .curPos = r .digests [0 ]
402433 }
403434
404435 // Look up the region associated with our current position.
@@ -426,15 +457,26 @@ func (t *thread) nextDigest() digest.Digest {
426457 }
427458
428459 next := r .digests [i ]
429- if loc , ok := t .def .Source .Locations [string (next )]; ! ok || len (loc .Locations ) == 0 {
430- // Skip this digest because it has no locations in the source file.
460+ if ! isValid (next ) {
431461 i ++
432462 continue
433463 }
434464 return next
435465 }
436466}
437467
468+ func (t * thread ) continueDigest () digest.Digest {
469+ if len (t .bps ) == 0 {
470+ return t .head
471+ }
472+
473+ isValid := func (dgst digest.Digest ) bool {
474+ _ , ok := t .bps [dgst ]
475+ return ok
476+ }
477+ return t .nextDigest (isValid )
478+ }
479+
438480func (t * thread ) solve (ctx context.Context , target digest.Digest ) (gateway.Reference , error ) {
439481 if target == t .head {
440482 return t .ref , nil
0 commit comments