Some checks failed
Docs Deploy / build_and_deploy (push) Has been cancelled
Generate Docs / cli (push) Has been cancelled
Generate Config Doc / cli (push) Has been cancelled
Go formatting / go-formatting (push) Has been cancelled
Check links / markdown-link-check (push) Has been cancelled
Integration / pre-test (push) Has been cancelled
Integration / test on (push) Has been cancelled
Integration / status (push) Has been cancelled
Lint / Lint Go code (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
184 lines
4.2 KiB
Go
184 lines
4.2 KiB
Go
package debugger
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
|
|
"github.com/go-delve/delve/pkg/logflags"
|
|
"github.com/go-delve/delve/pkg/terminal"
|
|
"github.com/go-delve/delve/service"
|
|
"github.com/go-delve/delve/service/debugger"
|
|
"github.com/go-delve/delve/service/rpc2"
|
|
"github.com/go-delve/delve/service/rpccommon"
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
"git.cw.tr/mukan-network/mukan-ignite/ignite/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
// DefaultAddress defines the default debug server address.
|
|
DefaultAddress = "127.0.0.1:30500"
|
|
|
|
// DefaultWorkingDir defines the default directory to use as
|
|
// working dir when running the app binary that will be debugged.
|
|
DefaultWorkingDir = "."
|
|
)
|
|
|
|
// Option configures debugging.
|
|
type Option func(*debuggerOptions)
|
|
|
|
type debuggerOptions struct {
|
|
disconnectChan chan struct{}
|
|
address, workingDir string
|
|
listener net.Listener
|
|
binaryArgs []string
|
|
clientRunHook, serverStartHook func()
|
|
}
|
|
|
|
// Address sets the address for the debug server.
|
|
func Address(address string) Option {
|
|
return func(o *debuggerOptions) {
|
|
o.address = address
|
|
}
|
|
}
|
|
|
|
// DisconnectChannel sets the channel used by the server to signal when the client disconnects.
|
|
func DisconnectChannel(c chan struct{}) Option {
|
|
return func(o *debuggerOptions) {
|
|
o.disconnectChan = c
|
|
}
|
|
}
|
|
|
|
// Listener sets a custom listener to serve requests.
|
|
func Listener(l net.Listener) Option {
|
|
return func(o *debuggerOptions) {
|
|
o.listener = l
|
|
}
|
|
}
|
|
|
|
// WorkingDir sets the working directory of the new process.
|
|
func WorkingDir(path string) Option {
|
|
return func(o *debuggerOptions) {
|
|
o.workingDir = path
|
|
}
|
|
}
|
|
|
|
// BinaryArgs sets command line argument for the new process.
|
|
func BinaryArgs(args ...string) Option {
|
|
return func(o *debuggerOptions) {
|
|
o.binaryArgs = args
|
|
}
|
|
}
|
|
|
|
// ClientRunHook sets a function to be executed right before debug client is run.
|
|
func ClientRunHook(fn func()) Option {
|
|
return func(o *debuggerOptions) {
|
|
o.clientRunHook = fn
|
|
}
|
|
}
|
|
|
|
// ServerStartHook sets a function to be executed right before debug server starts.
|
|
func ServerStartHook(fn func()) Option {
|
|
return func(o *debuggerOptions) {
|
|
o.serverStartHook = fn
|
|
}
|
|
}
|
|
|
|
// Start starts a debug server.
|
|
func Start(ctx context.Context, binaryPath string, options ...Option) (err error) {
|
|
o := applyDebuggerOptions(options...)
|
|
|
|
listener := o.listener
|
|
if listener == nil {
|
|
var c net.ListenConfig
|
|
|
|
listener, err = c.Listen(ctx, "tcp", o.address)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer listener.Close()
|
|
}
|
|
|
|
if err = disableDelveLogging(); err != nil {
|
|
return err
|
|
}
|
|
|
|
server := rpccommon.NewServer(&service.Config{
|
|
Listener: listener,
|
|
AcceptMulti: false,
|
|
APIVersion: 2,
|
|
CheckLocalConnUser: true,
|
|
DisconnectChan: o.disconnectChan,
|
|
ProcessArgs: append([]string{binaryPath}, o.binaryArgs...),
|
|
Debugger: debugger.Config{
|
|
WorkingDir: o.workingDir,
|
|
Backend: "default",
|
|
},
|
|
})
|
|
|
|
if o.serverStartHook != nil {
|
|
o.serverStartHook()
|
|
}
|
|
|
|
if err = server.Run(); err != nil {
|
|
return errors.Errorf("failed to run debug server: %w", err)
|
|
}
|
|
|
|
defer server.Stop() //nolint:errcheck
|
|
|
|
// Wait until the context is done or the connected client disconnects
|
|
select {
|
|
case <-ctx.Done():
|
|
case <-o.disconnectChan:
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Run runs a debug client.
|
|
func Run(ctx context.Context, binaryPath string, options ...Option) error {
|
|
listener, conn := service.ListenerPipe()
|
|
defer listener.Close()
|
|
|
|
o := applyDebuggerOptions(options...)
|
|
|
|
options = append(options, Listener(listener))
|
|
g, ctx := errgroup.WithContext(ctx)
|
|
|
|
// Start the debugger server
|
|
g.Go(func() error {
|
|
return Start(ctx, binaryPath, options...)
|
|
})
|
|
|
|
// Start the debug client
|
|
g.Go(func() error {
|
|
client := rpc2.NewClientFromConn(conn)
|
|
term := terminal.New(client, nil)
|
|
|
|
if o.clientRunHook != nil {
|
|
o.clientRunHook()
|
|
}
|
|
|
|
_, err := term.Run()
|
|
return err
|
|
})
|
|
|
|
return g.Wait()
|
|
}
|
|
|
|
func applyDebuggerOptions(options ...Option) debuggerOptions {
|
|
o := debuggerOptions{
|
|
address: DefaultAddress,
|
|
workingDir: DefaultWorkingDir,
|
|
disconnectChan: make(chan struct{}),
|
|
}
|
|
for _, apply := range options {
|
|
apply(&o)
|
|
}
|
|
return o
|
|
}
|
|
|
|
func disableDelveLogging() error {
|
|
return logflags.Setup(false, "", "")
|
|
}
|