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 1 commit
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"]
43 changes: 43 additions & 0 deletions cmd/frontend/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//go:build debug

package main

import (
"context"
"fmt"
"io"
"os/exec"
"os/signal"
"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)
}
}()

s, cancel := signal.NotifyContext(ctx, syscall.SIGCONT)
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if instead of using a signal here, if we can look at /proc/self/status: https://man7.org/linux/man-pages/man5/proc_pid_status.5.html

There should be a field called TracerPid which would be non-zero when being traced.

IF this works, there'd be no need to interact with the process at all other than to have dlv attach to it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This works!

Out of curiosity, how did you know this / find it out?

Copy link
Member

Choose a reason for hiding this comment

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

I just looked up if there was a way to check if a process is being traced.

defer cancel()
<-s.Done()

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
}
53 changes: 53 additions & 0 deletions debug/debug.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash

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
(
pid=""
while [ -z "$pid" ]; do
sleep 0.5
pid="$(pgrep frontend)"
done

socat_logfile="$(mktemp)"
socat -v UNIX:"/proc/${pid}/root/dlv.sock" TCP-LISTEN:30157,reuseaddr,fork 2>"$socat_logfile" &
scatpid="$!"
trap "kill -9 ${scatpid}" EXIT

# Detect when vs code has connected, then send the CONT signal to the frontend
txt=""
while [ -z "$txt" ]; do
sleep 0.5
if ! [ -f "$socat_logfile" ]; then
continue
fi
txt="$(head -n1 "$socat_logfile")"
done

kill -s CONT "$pid"
wait "$scatpid"
) &

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