Commit Graph
38 Commits
Author SHA1 Message Date
Jonathan A. Sternberg fdfba3014d tests: add integration tests for dap build
This adds integration tests for the `dap build` command to test various
behavior associated with the command. We start the build and the
integration test acts as a dap client to send requests and check that
the output is what we expect.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-12-17 10:56:30 -06:00
Brian Goff 8d822ebf71 dap: Fix breakpoint overlap check
The old logic wasn't quite right, and a little hard to follow.
Before this, because breakpoints sent from setBreakpoints don't have an
end line or column they are just a single point the comparison would
fail the check due to the column check.

E.g. if I have a breakpoint on line 30.
The content lines range from 29-31.
Now lets say the end column is at postion 10, but my breakpoint is at
L30 column 11 (still within range).
The overlap check would fail because 11 > 10 but these shouldn't be
compared at all in this case.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2025-11-24 17:03:26 -08:00
Tõnis TiigiandGitHub cd38f981c7 Merge pull request #3488 from jsternberg/dap-step-out-skips-breakpoints
dap: next and out now respect breakpoints
2025-11-03 09:38:00 -08:00
Jonathan A. Sternberg 9099a030f4 dap: attempt to find multiple digests when a failure happens
The solve error that gets returned sometimes has a platform when the LLB
returned by `ToState` doesn't. In order to ensure we find the failed
digest, we now search for both the digest of what was returned and also
clear out the platform and search for the digest without that in case
that one matches instead.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-10-29 16:08:46 -05:00
Jonathan A. Sternberg 5b8b65a59c dap: next and out now respect breakpoints
If a breakpoint occurs before the step pointed to by next or out, dap
will now stop there instead of the desired location.

This also updates the loop to always set the breakpoints rather than
only when continue is chosen.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-10-29 15:20:15 -05:00
Tõnis TiigiandGitHub 00502c0199 Merge pull request #3483 from jsternberg/dap-eval-dependencies
dap: refactor how steps are evaluated in the program
2025-10-29 13:18:57 -07:00
Jonathan A. Sternberg 4345a8e50b dap: refactor how steps are evaluated in the program
This change modifies how steps are evaluated in the program. Previously,
the execution relied on looking for the digest we were going to stop at
and evaluating the step right before that digest. This could result in
some gaps where it erroneously didn't execute inputs for the current
step as it skipped those digests.

Now, the evaluation reads the inputs and executed those directly rather
than relying on the evaluation of previous steps in the sequence. This
should make the evaluation of inputs more accurate and allow us to have
better breakpoints on things like the copy operation.

Due to the change, it's also easier for us to include the different
inputs in the file explorer.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-10-24 11:13:25 -05:00
Remy Suen 2af3005f52 Initialize the breakpoints map in the beginning
Signed-off-by: Remy Suen <remy.suen@docker.com>
2025-10-23 10:44:05 -04:00
Remy Suen 7c4dd41cb0 dap: stop sending null to conform to the specification
The setBreakpoints response body is defined to be an array of
breakpoints but the debug adapter incorrectly serialized an empty array
as null in the JSON message. Explicitly initializing the array will
force the JSON serialization process to send an empty array back instead
of null.

Signed-off-by: Remy Suen <remy.suen@docker.com>
2025-10-23 05:31:37 -04:00
Jonathan A. Sternberg 6ac8458692 dap: support run in terminal request when buildx is run in standalone mode
The run in terminal request sent by exec would not work if `buildx` had
been invoked directly instead of through `docker`. If we don't find the
reexec environment variable, we use `os.Args[0]` to launch buildx
directly when invoking attach.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-10-15 12:03:49 -05:00
Jonathan A. Sternberg 92ea5e64b4 dap: file explorer uses the wrong path when looking up mounts
It accidentally retrieved the relative path to the mount directory
instead of the full path which resulted in always returning the root
directory.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-09-24 09:28:38 -05:00
Jonathan A. Sternberg 17c2e1dc35 dap: collect references for inputs to ensure the file explorer works on an error
The debug adapter will now look at the error and collect the input
mounts from the exec operation and translate them to a gateway reference
to be able to access the filesystem in the file explorer.

When we are attempting to find the real operation that failed and
succeed, we now re-solve the dependencies (which should already have
been solved correctly to begin with) so that we can have the proper
reference and can access the filesystem contents.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-09-17 15:58:28 -05:00
Tõnis TiigiandGitHub f4e02addf1 Merge pull request #3408 from jsternberg/dap-better-location-resolution
dap: improve location resolution for duplicate digests
2025-09-10 13:26:03 -07:00
Jonathan A. SternbergandGitHub f55ed6015b Merge pull request #3412 from crazy-max/dap-fix-data-path
dap: use slash separator when reading ref files
2025-09-08 09:42:07 -05:00
CrazyMax 90076e1d07 dap: use slash separator when reading ref files
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-09-08 15:04:51 +02:00
Jonathan A. Sternberg bf40c24e00 dap: improve location resolution for duplicate digests
This improves location resolution for duplicate digests by making some
assumptions about the file structure to determine a "best match" when
there are multiple possible locations.

It uses the next operation (that this digest is the input for) to
determine a location. If the location of the current operation is before
the next operation, this location is preferred. If there are multiple
locations that happen before the next operation, the one closest to the
next operation (aka later in the file) is used instead.

This resolves the most common case of multiple identical `FROM`
statements without adding more code to the frontends themselves.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-09-04 15:04:06 -05:00
Jonathan A. Sternberg 8841b2dfc8 dap: ensure test client is closed on cleanup
The dap test wasn't waiting for the client's goroutines to complete
before exiting which caused a race condition that could cause it to log
to the dead test logger. This became apparent when `--count` of greater
than one was used since it caused the test to run long enough to trigger
the behavior. It would have also triggered if we had added more tests.

Add the client close to the cleanup so it waits for the goroutine to
finish before the test exits as it was properly supposed to do.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-09-03 10:51:01 -05:00
Tonis Tiigi 440dc2a212 temp skip DAP test that panics in errgroup goroutine
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-08-27 14:38:37 -07:00
Tõnis TiigiandGitHub bac71def78 Merge pull request #3366 from jsternberg/dap-detect-parent
dap: improve determination of the proper parent for certain ops
2025-08-18 18:34:57 +03:00
Jonathan A. Sternberg 5c97696d64 dap: improve determination of the proper parent for certain ops
Improves the determination of the proper parent for exec and file ops.
With file ops, it will only consider inputs and ignore secondary inputs.
This prevents the following case:

```
FROM busybox AS build1
RUN echo foo > /hello

FROM scratch
COPY --from=build1 /hello .
```

Previously, `build1` would be considered the parent of the copy
instruction. Now, copy properly does not have a parent.

If there are multiple file ops and the operations disagree on the
canonical "parent", we give up on trying to find a canonical parent and
assume there is none.

For exec operations, whichever input is associated with the root mount
is considered the primary parent.

For all other operations, the first parent is considered the primary
parent if it exists.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-08-18 09:27:02 -05:00
Jonathan A. Sternberg b3c389690c dap: look for base name of dockerfile name instead of path from context
When the builder loads a dockerfile, it does it by using the base name
of the dockerfile path and only loads the innermost directory. This
means that the source name we're looking for is the base name and not
the full relative path.

Update the set breakpoints functionality so it takes this into account.
Fixes scenarios where DAP is used with a dockerfile nested in the
context.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-08-15 14:41:00 -05:00
Jonathan A. Sternberg dbda218489 dap: make exec shell persistent across the build
Invoking the shell will cause it to persist across the entire build and
to re-execute whenever the builder pauses at another location again.

This still requires using `exec` to launch the shell. Launching by frame
id is also removed since it no longer applies to this version.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-08-11 12:40:09 -05:00
Jonathan A. Sternberg 8e356c3454 dap: filesystem inspection when paused on a digest
Add a file explorer to the debugger that allows exploring the filesystem
of the current container. It will show directory contents, file
contents, and symlink destinations. It will also show the file mode
associated with a file.

The file explorer defaults to marking itself as an expensive operation
so the debugger doesn't automatically retrieve the information.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-07-28 09:52:30 -05:00
Jonathan A. Sternberg 1e3c44709d dap: refactor how step in/step out works
Change how breakpoints and stepping works. These now work more how you
would expect another programming language to work. Breakpoints happen
before the step has been invoked rather than after which means you can
inspect the state before the command runs.

This has the advantage of being more intuitive for someone familiar with
other debuggers. The negative is that you can't run to after a certain
step as easily as you could before. Instead, you would run to that stage
and then use next to go to the step directly afterwards.

Step in and out also now have different behaviors. When a step has
multiple inputs, the inputs of non-zero index are considered like
"function calls". The most common cause of this is to use `COPY --from`
or a bind mount. Stepping into these will cause it to jump to the
beginning of the call chain for that branch. Using step out will exit
back to the location where step in was used.

This change also makes it so some steps may be invoked multiple times in
the callgraph if multiple steps depend on them. The reused steps will
still be cached, but you may end up stepping through more lines than the
previous implementation.

Stack traces now represent where these step in and step out areas
happen rather than the previous steps. This can help you know from where
a certain step is being used.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-07-23 17:10:40 -05:00
Jonathan A. Sternberg fea53ad1f8 dap: return error from evaluate command in repl context
In the repl context, we will now return the error instead of directly
printing it. We also suppress reporting errors from cobra. The logic
flow has also been changed to prevent returning errors from cobra unless
there was something related to the command line invocation so usage will
only be printed when a command was typed wrong and it will not show up
for every error.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-07-23 13:51:03 -05:00
Jonathan A. Sternberg 3453f3b00a dap: support evaluate request to invoke a container
Supports using the `evaluate` request in REPL mode to start a container
with the `exec` command. Presently doesn't support any arguments.

This improves the dap server so it is capable of sending reverse
requests and receiving the response. It also adds a hidden command
`dap attach` that attaches to the socket created by `evaluate`.

This requires the client to support `runInTerminal`.

Likely needs some additional work to make sure resources are cleaned up
cleanly especially when the build is unpaused or terminated, but it
should work as a decent base.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-07-15 10:45:25 -05:00
Tõnis TiigiandGitHub 9a2207a692 Merge pull request #3313 from jsternberg/dap-variable-reference-fix
dap: do not modify variable references on variables that are zero
2025-07-14 13:33:22 -07:00
Jonathan A. Sternberg e9d4b86161 dap: always return the error from execution if we paused on an error and resume
Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-07-14 14:00:02 -05:00
Jonathan A. Sternberg 7925996c0c dap: do not modify variable references on variables that are zero
Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-07-14 13:57:26 -05:00
Tõnis TiigiandGitHub ebe66f6922 Merge pull request #3309 from jsternberg/dap-step-into
dap: alias step into to next and step out to continue
2025-07-14 09:55:27 -07:00
Jonathan A. Sternberg 0a78659776 dap: alias step into to next and step out to continue
Step into and step out are required by the UI for DAP. We don't have a
way to implement these in a logical manner but they need to exist. We'll
discuss in further iterations how these might differ from next and
continue, but for now, we just need some implementation for the UI.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-07-14 11:00:33 -05:00
Jonathan A. Sternberg 1886e232c5 dap: implement variable references
Implement variable references to inspect the state of a stack frame.

Variable reference ids are composed of two sections. A thread mask that
is the first 8 bytes and the remainder is an increasing number that gets
reset each time a thread is resumed. This allows the adapter to know
which thread to delegate the variables request to and allows the
variable references to still remain confined to each thread. An int32 is
used for this because variable references need to be in the range of
(0, 2^32).

At the moment, only the platform variables and some of the exec
operations for an operation. These are labeled as "arguments" to the
stack frame.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-07-14 10:59:05 -05:00
Jonathan A. Sternberg 0dddf0a7b8 dap: implement first pass at breakpoints
Implement the first iteration of breakpoints. When a breakpoint is set,
it starts unverified. When a thread begins evaluation, it tries to see
if a breakpoint corresponds to one of the parsed instructions and will
verify it.

Breakpoints work when continue is used.

At the current moment, setting breakpoints while a thread is currently
running doesn't work. Breakpoints are rechecked each time execution is
about to restart.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-07-14 09:19:17 -05:00
Jonathan A. Sternberg a291698eaf dap: support stopOnEntry to configure behavior when starting the debugger
This will configure the default behavior when beginning to evaluate a
build target. When `stopOnEntry` is used, it will default to `stepNext`.
Otherwise, `stepContinue` will be used.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-07-07 09:39:23 -05:00
Tõnis TiigiandGitHub 0c747263ef Merge pull request #3279 from jsternberg/dap-step
dap: add stack traces with next and continue functionality
2025-07-03 18:13:13 -07:00
Jonathan A. Sternberg 4f2e23a9b8 dap: add stack traces with next and continue functionality
It is now possible to send next and continue as separate signals. When
executing a build, the debug adapter will divide the LLB graph into
regions. Each region corresponds to an uninterrupted chain of
instructions. It will also record which regions depend on which other
ones.

This determines the execution order and it also determines what the
stack traces look like.

When continue is used, we will attempt to evaluate the last leaf node
(the head). If we push next, we will determine which digest would be the
next one to be processed.

In the future, this will be used to also support breakpoints.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-07-03 09:18:55 -05:00
Jonathan A. Sternberg f03ed8cc9f dap: increase timeout for receiving configuration done in adapter test
Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-07-03 09:12:34 -05:00
Jonathan A. Sternberg 42599a7d49 dap: add debug adapter implementation
Adds a simple implementation of the debug adapter that supports the very
basics of a debug adapter.

It supports the launch request, the configuration done request, the
creation of threads, stopping, resuming, and disconnecting from server.

It does not support custom breakpoints, stack traces, or variable
inspection yet. These are planned to be added in the future.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-06-30 10:51:20 -05:00