Skip to content
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

feat: Add debugging setup for vscode #465

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions Dockerfile.debug
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.22@sha256:9e5243001d0f43d6e1c556a0ce8eedc1bc80eb37b5210036b289d2c601bc08b6 AS go
Copy link
Member

@cpuguy83 cpuguy83 Dec 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we unify the dockerfiles and pass the debug in as a build-arg?

if [ "${DEBUG}" = "1" ]; then
    extra_flags=...
fi
go build ${extra_fiags} ...

FROM scratch AS dlv-ctx

FROM go AS frontend-build
ARG HOSTDIR
WORKDIR ${HOSTDIR}
COPY . .
ENV CGO_ENABLED=0
ARG TARGETARCH TARGETOS GOFLAGS=-trimpath
ENV GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOFLAGS=${GOFLAGS}
RUN \
--mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build -tags=debug -gcflags="all=-N -l" -o /frontend ./cmd/frontend && \
go build -o /dalec-redirectio ./cmd/dalec-redirectio

FROM go AS dlv-build
ARG HOSTDIR
WORKDIR /dlv
ADD https://github.com/go-delve/delve.git#v1.23.1 .
ENV CGO_ENABLED=0
ARG TARGETARCH TARGETOS GOFLAGS=-trimpath
ENV GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOFLAGS=${GOFLAGS}
RUN \
--mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
make build

FROM mcr.microsoft.com/azurelinux/distroless/base:3.0 AS frontend
ARG HOSTDIR
COPY . ${HOSTDIR}
COPY --from=frontend-build /frontend /frontend
COPY --from=frontend-build /dalec-redirectio /dalec-redirectio
COPY --from=dlv-build /dlv/dlv /usr/local/bin/dlv
LABEL moby.buildkit.frontend.network.none="true"
LABEL moby.buildkit.frontend.caps="moby.buildkit.frontend.inputs,moby.buildkit.frontend.subrequests,moby.buildkit.frontend.contexts"
ENTRYPOINT ["/frontend"]
64 changes: 64 additions & 0 deletions cmd/frontend/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//go:build debug

package main

import (
"bufio"
"context"
"fmt"
"io"
"os"
"os/exec"
"strings"
"syscall"
)

func waitForDebug(ctx context.Context) error {
pid := fmt.Sprintf("%d", syscall.Getpid())
cmd := exec.Command(
"dlv",
"attach",
"--api-version=2",
"--headless",
"--listen=unix:/dlv.sock",
"--allow-non-terminal-interactive",
pid,
)
cmd.Stdout = io.Discard
cmd.Stderr = io.Discard

if err := cmd.Start(); err != nil {
return err
}

go func() {
if err := cmd.Wait(); err != nil {
panic(err)
}
}()

tracerPid := "0"
for tracerPid == "0" {
b, err := os.Open("/proc/self/status")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we ensure the file gets closed? i.e. defer b.Close()

if err != nil {
return err
}

s := bufio.NewScanner(b)
for s.Scan() {
start, end, ok := strings.Cut(s.Text(), ":\t")
if !ok {
continue
}

if start != "TracerPid" {
continue
}

tracerPid = end
break
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a short sleep here, like 100ms?

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to check s.Err() after the scan loop finishes.


return nil
}
31 changes: 22 additions & 9 deletions cmd/frontend/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
_ "embed"
"os"

Expand All @@ -10,6 +11,7 @@ import (
"github.com/Azure/dalec/frontend/debug"
"github.com/Azure/dalec/frontend/ubuntu"
"github.com/Azure/dalec/frontend/windows"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/frontend/gateway/grpcclient"
"github.com/moby/buildkit/util/appcontext"
"github.com/moby/buildkit/util/bklog"
Expand All @@ -31,16 +33,27 @@ func main() {

mux.Add(debug.DebugRoute, debug.Handle, nil)

if err := grpcclient.RunFromEnvironment(ctx, mux.Handler(
// copy/paster's beware: [frontend.WithTargetForwardingHandler] should not be set except for the root dalec frontend.
frontend.WithBuiltinHandler(azlinux.Mariner2TargetKey, azlinux.NewMariner2Handler()),
frontend.WithBuiltinHandler(azlinux.AzLinux3TargetKey, azlinux.NewAzlinux3Handler()),
frontend.WithBuiltinHandler(windows.DefaultTargetKey, windows.Handle),
ubuntu.Handlers,
debian.Handlers,
frontend.WithTargetForwardingHandler,
)); err != nil {
f := func(ctx context.Context, client gwclient.Client) (*gwclient.Result, error) {
if err := waitForDebug(ctx); err != nil {
return nil, err
}

handlerFunc := mux.Handler(
// copy/paster's beware: [frontend.WithTargetForwardingHandler] should not be set except for the root dalec frontend.
frontend.WithBuiltinHandler(azlinux.Mariner2TargetKey, azlinux.NewMariner2Handler()),
frontend.WithBuiltinHandler(azlinux.AzLinux3TargetKey, azlinux.NewAzlinux3Handler()),
frontend.WithBuiltinHandler(windows.DefaultTargetKey, windows.Handle),
ubuntu.Handlers,
debian.Handlers,
frontend.WithTargetForwardingHandler,
)

return handlerFunc(ctx, client)
}

if err := grpcclient.RunFromEnvironment(ctx, f); err != nil {
bklog.L.WithError(err).Fatal("error running frontend")
os.Exit(137)
}

}
9 changes: 9 additions & 0 deletions cmd/frontend/no_debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build !debug

package main

import "context"

func waitForDebug(ctx context.Context) error {
return nil
}
43 changes: 43 additions & 0 deletions debug/debug.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
set -eux

MorrisLaw marked this conversation as resolved.
Show resolved Hide resolved
if [ -z "$(command -v socat)" ]; then
echo you must have "'socat'" installed
exit 1
fi

if [ -z "$(command -v pgrep)" ]; then
echo you must have "'pgrep'" installed
exit 1
fi

PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${PROJECT_DIR}"

# Build frontend with debugging setup Note the host path for the dalec source
# and the in-container build path must be the same
REF="local/dalec/frontend:tmp"
docker build \
-f Dockerfile.debug \
-t "${REF}" \
--build-arg=HOSTDIR="${PROJECT_DIR}" \
.

# Wait for frontend process to start, and forward the socket connection when the process has started
(
set +x
pid=""
while [ -z "$pid" ]; do
sleep 0.5
pid="$(pgrep frontend)"
done
set -x

socat_logfile="$(mktemp)"
socat -v UNIX:"/proc/${pid}/root/dlv.sock" TCP-LISTEN:30157,reuseaddr,fork 2>"$socat_logfile"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cpuguy83 can you think of a better way to connect vscode to the dlv instance running in the container? This isn't very portable. The vscode launch.json can't do much dynamically in the way of finding the port, so we need the port to be consistent across runs.

socat_pid="$!"
trap "kill -9 ${socat_pid}" EXIT
) &

# Run the build
exec docker build --build-arg=BUILDKIT_SYNTAX="${REF}" "$@"
Loading