Support breakpoint debugger integrated to IDEs

This commit adds the IDE-integrated breakpoint debugger based on walker.
Now buildx provides DAP (Debug Adapter Protocol) API to IDEs so DAP-aware IDEs
can call buildx and allow users to perform breakpoint-based debugging on the
IDE's UI/UX.

Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
This commit is contained in:
Kohei Tokunaga
2023-06-30 21:32:20 +09:00
parent bcf21dee44
commit 37763ec29f
81 changed files with 18561 additions and 19 deletions

1190
monitor/dap/dap.go Normal file

File diff suppressed because it is too large Load Diff

143
monitor/dap/io.go Normal file
View File

@@ -0,0 +1,143 @@
//go:build linux
package dap
// Ported from https://github.com/ktock/buildg/blob/v0.4.1/pkg/dap/dap.go
// Copyright The buildg Authors.
// Licensed under the Apache License, Version 2.0
import (
"context"
"fmt"
"io"
"path/filepath"
"syscall"
"time"
"github.com/containerd/console"
"github.com/containerd/fifo"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
)
func AttachContainerIO(root string, stdin io.Reader, stdout, stderr io.Writer, setTtyRaw bool) error {
if root == "" {
return errors.Errorf("root needs to be specified")
}
type ioSet struct {
stdin io.WriteCloser
stdout io.ReadCloser
stderr io.ReadCloser
}
ioSetCh := make(chan ioSet)
errCh := make(chan error)
go func() {
stdin, stdout, stderr, err := openFifosClient(context.TODO(), root)
if err != nil {
errCh <- err
return
}
ioSetCh <- ioSet{stdin, stdout, stderr}
}()
var (
pStdin io.WriteCloser
pStdout io.ReadCloser
pStderr io.ReadCloser
)
select {
case ioSet := <-ioSetCh:
pStdin, pStdout, pStderr = ioSet.stdin, ioSet.stdout, ioSet.stderr
case err := <-errCh:
return err
case <-time.After(3 * time.Second):
return errors.Errorf("i/o timeout; check server is up and running")
}
defer func() { pStdin.Close(); pStdout.Close(); pStderr.Close() }()
if setTtyRaw {
con := console.Current()
if err := con.SetRaw(); err != nil {
return errors.Errorf("failed to configure terminal: %v", err)
}
defer con.Reset()
}
go io.Copy(pStdin, stdin)
eg, _ := errgroup.WithContext(context.TODO())
eg.Go(func() error { _, err := io.Copy(stdout, pStdout); return err })
eg.Go(func() error { _, err := io.Copy(stderr, pStderr); return err })
if err := eg.Wait(); err != nil {
return err
}
fmt.Fprintf(stderr, "exec finished\n")
return nil
}
func serveContainerIO(ctx context.Context, root string) (io.ReadCloser, io.WriteCloser, io.WriteCloser, func(), error) {
stdin, stdout, stderr, err := openFifosServer(ctx, root)
if err != nil {
return nil, nil, nil, nil, err
}
return stdin, stdout, stderr, func() {
stdin.Close()
stdout.Close()
stderr.Close()
}, nil
}
func openFifosClient(ctx context.Context, fifosDir string) (stdin io.WriteCloser, stdout, stderr io.ReadCloser, retErr error) {
if stdin, retErr = fifo.OpenFifo(ctx, filepath.Join(fifosDir, "stdin"), syscall.O_WRONLY, 0700); retErr != nil {
return nil, nil, nil, errors.Errorf("failed to open stdin fifo: %v", retErr)
}
defer func() {
if retErr != nil && stdin != nil {
stdin.Close()
}
}()
if stdout, retErr = fifo.OpenFifo(ctx, filepath.Join(fifosDir, "stdout"), syscall.O_RDONLY, 0700); retErr != nil {
return nil, nil, nil, errors.Errorf("failed to open stdout fifo: %v", retErr)
}
defer func() {
if retErr != nil && stdout != nil {
stdout.Close()
}
}()
if stderr, retErr = fifo.OpenFifo(ctx, filepath.Join(fifosDir, "stderr"), syscall.O_RDONLY, 0700); retErr != nil {
return nil, nil, nil, errors.Errorf("failed to open stderr fifo: %v", retErr)
}
defer func() {
if retErr != nil && stderr != nil {
stderr.Close()
}
}()
return stdin, stdout, stderr, nil
}
func openFifosServer(ctx context.Context, fifosDir string) (stdin io.ReadCloser, stdout, stderr io.WriteCloser, retErr error) {
if stdin, retErr = fifo.OpenFifo(ctx, filepath.Join(fifosDir, "stdin"), syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); retErr != nil {
return nil, nil, nil, errors.Errorf("failed to open stdin fifo: %v", retErr)
}
defer func() {
if retErr != nil && stdin != nil {
stdin.Close()
}
}()
if stdout, retErr = fifo.OpenFifo(ctx, filepath.Join(fifosDir, "stdout"), syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); retErr != nil {
return nil, nil, nil, errors.Errorf("failed to open stdout fifo: %v", retErr)
}
defer func() {
if retErr != nil && stdout != nil {
stdout.Close()
}
}()
if stderr, retErr = fifo.OpenFifo(ctx, filepath.Join(fifosDir, "stderr"), syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); retErr != nil {
return nil, nil, nil, errors.Errorf("failed to open stderr fifo: %v", retErr)
}
defer func() {
if retErr != nil && stderr != nil {
stderr.Close()
}
}()
return stdin, stdout, stderr, nil
}

18
monitor/dap/io_nolinux.go Normal file
View File

@@ -0,0 +1,18 @@
//go:build !linux
package dap
import (
"context"
"io"
"github.com/pkg/errors"
)
func AttachContainerIO(root string, stdin io.Reader, stdout, stderr io.Writer, setTtyRaw bool) error {
return errors.Errorf("unsupported")
}
func serveContainerIO(ctx context.Context, root string) (io.ReadCloser, io.WriteCloser, io.WriteCloser, func(), error) {
return nil, nil, nil, nil, errors.Errorf("unsupported")
}