2016-02-19 18:32:24 +00:00
package cmds
import (
2020-08-31 17:51:10 +00:00
"encoding/json"
2016-02-19 18:32:24 +00:00
"errors"
"fmt"
2024-04-09 18:04:55 +00:00
"log"
2016-02-19 18:32:24 +00:00
"net"
"os"
"os/exec"
"os/signal"
"path/filepath"
2021-07-31 15:16:26 +00:00
"reflect"
2016-02-19 18:32:24 +00:00
"runtime"
"strconv"
2020-05-13 06:38:10 +00:00
"strings"
2016-02-19 18:32:24 +00:00
"syscall"
2023-05-08 17:41:47 +00:00
"time"
2016-02-19 18:32:24 +00:00
2023-08-09 17:37:55 +00:00
"github.com/go-delve/delve/cmd/dlv/cmds/helphelpers"
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/pkg/config"
2020-03-04 17:22:51 +00:00
"github.com/go-delve/delve/pkg/gobuild"
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/pkg/goversion"
"github.com/go-delve/delve/pkg/logflags"
2023-07-05 15:39:01 +00:00
"github.com/go-delve/delve/pkg/proc"
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/pkg/terminal"
"github.com/go-delve/delve/pkg/version"
"github.com/go-delve/delve/service"
"github.com/go-delve/delve/service/api"
2020-02-15 19:52:53 +00:00
"github.com/go-delve/delve/service/dap"
2020-04-13 18:07:15 +00:00
"github.com/go-delve/delve/service/debugger"
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/service/rpc2"
"github.com/go-delve/delve/service/rpccommon"
2020-08-21 14:14:02 +00:00
"github.com/mattn/go-isatty"
2016-02-19 18:32:24 +00:00
"github.com/spf13/cobra"
)
var (
2024-04-09 18:04:55 +00:00
// logFlag is whether to log debug statements.
logFlag bool
2020-03-31 22:10:37 +00:00
// logOutput is a comma separated list of components that should produce debug output.
logOutput string
// logDest is the file path or file descriptor where logs should go.
logDest string
// headless is whether to run without terminal.
headless bool
// continueOnStart is whether to continue the process on startup
continueOnStart bool
// apiVersion is the requested API version while running headless
apiVersion int
// acceptMulti allows multiple clients to connect to the same server
acceptMulti bool
// addr is the debugging server listen address.
addr string
// initFile is the path to initialization file.
initFile string
// buildFlags is the flags passed during compiler invocation.
buildFlags string
// workingDir is the working directory for running the program.
workingDir string
// checkLocalConnUser is true if the debugger should check that local
2020-02-11 17:06:29 +00:00
// connections come from the same user that started the headless server
2020-03-31 22:10:37 +00:00
checkLocalConnUser bool
2020-04-04 22:19:35 +00:00
// tty is used to provide an alternate TTY for the program you wish to debug.
tty string
2020-10-21 19:50:52 +00:00
// disableASLR is used to disable ASLR
disableASLR bool
2016-02-19 18:32:24 +00:00
2021-10-13 18:43:47 +00:00
// dapClientAddr is dap subcommand's flag that specifies the address of a DAP client.
// If it is specified, the dap server starts a debug session by dialing to the client.
// The dap server will serve only for the debug session.
dapClientAddr string
2020-03-31 22:10:37 +00:00
// backend selection
backend string
2017-02-10 14:11:40 +00:00
2020-03-31 22:10:37 +00:00
// checkGoVersion is true if the debugger should check the version of Go
2019-04-26 17:24:21 +00:00
// used to compile the executable and refuse to work on incompatible
// versions.
2020-03-31 22:10:37 +00:00
checkGoVersion bool
2019-04-26 17:24:21 +00:00
2020-03-31 22:10:37 +00:00
// rootCommand is the root of the command tree.
rootCommand * cobra . Command
2016-02-19 18:32:24 +00:00
2023-05-08 17:41:47 +00:00
traceAttachPid int
traceExecFile string
traceTestBinary bool
traceStackDepth int
traceUseEBPF bool
traceShowTimestamp bool
2024-06-12 19:35:48 +00:00
traceFollowCalls int
2016-02-19 18:32:24 +00:00
2020-08-21 14:14:02 +00:00
// redirect specifications for target process
redirects [ ] string
allowNonTerminalInteractive bool
2021-10-14 01:46:20 +00:00
conf * config . Config
loadConfErr error
2023-03-01 19:28:32 +00:00
rrOnProcessPid int
2023-08-09 17:30:22 +00:00
attachWaitFor string
attachWaitForInterval float64
attachWaitForDuration float64
2016-02-19 18:32:24 +00:00
)
const dlvCommandLongDesc = ` Delve is a source level debugger for Go programs .
Delve enables you to interact with your program by controlling the execution of the process ,
evaluating variables , and providing information of thread / goroutine state , CPU register state and more .
The goal of this tool is to provide a simple yet powerful interface for debugging Go programs .
2017-02-16 19:05:48 +00:00
Pass flags to the program you are debugging using ` + " ` -- ` " + ` , for example :
` + " ` dlv exec . / hello -- server -- config conf / config . toml ` "
2016-02-19 18:32:24 +00:00
// New returns an initialized command tree.
2017-06-20 17:36:07 +00:00
func New ( docCall bool ) * cobra . Command {
2016-02-19 18:32:24 +00:00
// Config setup and load.
2021-10-14 01:46:20 +00:00
conf , loadConfErr = config . LoadConfig ( )
// Delay reporting errors about configuration loading delayed until after the
// server is started so that the "server listening at" message is always
2023-11-03 16:22:02 +00:00
// the first thing emitted. Also, logflags hasn't been set up yet at this point.
2016-02-19 18:32:24 +00:00
buildFlagsDefault := ""
if runtime . GOOS == "windows" {
2017-05-04 10:22:08 +00:00
ver , _ := goversion . Installed ( )
2020-03-18 16:25:32 +00:00
if ver . Major > 0 && ! ver . AfterOrEqual ( goversion . GoVersion { Major : 1 , Minor : 9 , Rev : - 1 } ) {
2017-05-04 10:22:08 +00:00
// Work-around for https://github.com/golang/go/issues/13154
buildFlagsDefault = "-ldflags='-linkmode internal'"
}
2016-02-19 18:32:24 +00:00
}
// Main dlv root command.
2020-03-31 22:10:37 +00:00
rootCommand = & cobra . Command {
2016-02-19 18:32:24 +00:00
Use : "dlv" ,
Short : "Delve is a debugger for the Go programming language." ,
Long : dlvCommandLongDesc ,
}
2024-02-07 19:04:45 +00:00
rootCommand . PersistentFlags ( ) . StringVarP ( & addr , "listen" , "l" , "127.0.0.1:0" , "Debugging server listen address. Prefix with 'unix:' to use a unix domain socket." )
2024-04-09 18:04:55 +00:00
must ( rootCommand . RegisterFlagCompletionFunc ( "listen" , cobra . NoFileCompletions ) )
2019-03-27 21:58:36 +00:00
2024-04-09 18:04:55 +00:00
rootCommand . PersistentFlags ( ) . BoolVarP ( & logFlag , "log" , "" , false , "Enable debugging server logging." )
2020-03-31 22:10:37 +00:00
rootCommand . PersistentFlags ( ) . StringVarP ( & logOutput , "log-output" , "" , "" , ` Comma separated list of components that should produce debug output (see 'dlv help log') ` )
2024-04-09 18:04:55 +00:00
must ( rootCommand . RegisterFlagCompletionFunc ( "log-output" , cobra . FixedCompletions ( [ ] string { "debugger" , "gdbwire" , "lldbout" , "debuglineerr" , "rpc" , "dap" , "fncall" , "minidump" , "stack" } , cobra . ShellCompDirectiveNoFileComp ) ) )
2020-03-31 22:10:37 +00:00
rootCommand . PersistentFlags ( ) . StringVarP ( & logDest , "log-dest" , "" , "" , "Writes logs to the specified file or file descriptor (see 'dlv help log')." )
2024-04-09 18:04:55 +00:00
must ( rootCommand . MarkPersistentFlagFilename ( "log-dest" , "log" ) )
2019-03-27 21:58:36 +00:00
2022-01-06 16:57:40 +00:00
rootCommand . PersistentFlags ( ) . BoolVarP ( & headless , "headless" , "" , false , "Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections." )
rootCommand . PersistentFlags ( ) . BoolVarP ( & acceptMulti , "accept-multiclient" , "" , false , "Allows a headless server to accept multiple client connections via JSON-RPC or DAP." )
rootCommand . PersistentFlags ( ) . IntVar ( & apiVersion , "api-version" , 1 , "Selects JSON-RPC API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md." )
2024-04-09 18:04:55 +00:00
must ( rootCommand . RegisterFlagCompletionFunc ( "api-version" , cobra . FixedCompletions ( [ ] string { "1" , "2" } , cobra . ShellCompDirectiveNoFileComp ) ) )
2020-03-31 22:10:37 +00:00
rootCommand . PersistentFlags ( ) . StringVar ( & initFile , "init" , "" , "Init file, executed by the terminal client." )
2024-04-09 18:04:55 +00:00
must ( rootCommand . MarkPersistentFlagFilename ( "init" ) )
2021-01-05 18:57:30 +00:00
rootCommand . PersistentFlags ( ) . StringVar ( & buildFlags , "build-flags" , buildFlagsDefault , "Build flags, to be passed to the compiler. For example: --build-flags=\"-tags=integration -mod=vendor -cover -v\"" )
2024-04-09 18:04:55 +00:00
must ( rootCommand . RegisterFlagCompletionFunc ( "build-flags" , cobra . NoFileCompletions ) )
2020-08-31 17:51:10 +00:00
rootCommand . PersistentFlags ( ) . StringVar ( & workingDir , "wd" , "" , "Working directory for running the program." )
2024-04-09 18:04:55 +00:00
must ( rootCommand . MarkPersistentFlagDirname ( "wd" ) )
2021-09-25 15:41:00 +00:00
rootCommand . PersistentFlags ( ) . BoolVarP ( & checkGoVersion , "check-go-version" , "" , true , "Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve." )
2020-03-31 22:10:37 +00:00
rootCommand . PersistentFlags ( ) . BoolVarP ( & checkLocalConnUser , "only-same-user" , "" , true , "Only connections from the same user that started this instance of Delve are allowed to connect." )
rootCommand . PersistentFlags ( ) . StringVar ( & backend , "backend" , "default" , ` Backend selection (see 'dlv help backend'). ` )
2024-04-09 18:04:55 +00:00
must ( rootCommand . RegisterFlagCompletionFunc ( "backend" , cobra . FixedCompletions ( [ ] string { "default" , "native" , "lldb" , "rr" } , cobra . ShellCompDirectiveNoFileComp ) ) )
2020-08-21 14:14:02 +00:00
rootCommand . PersistentFlags ( ) . StringArrayVarP ( & redirects , "redirect" , "r" , [ ] string { } , "Specifies redirect rules for target process (see 'dlv help redirect')" )
2024-04-09 18:04:55 +00:00
must ( rootCommand . MarkPersistentFlagFilename ( "redirect" ) )
2020-08-21 14:14:02 +00:00
rootCommand . PersistentFlags ( ) . BoolVar ( & allowNonTerminalInteractive , "allow-non-terminal-interactive" , false , "Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr" )
2020-10-21 19:50:52 +00:00
rootCommand . PersistentFlags ( ) . BoolVar ( & disableASLR , "disable-aslr" , false , "Disables address space randomization" )
2016-02-19 18:32:24 +00:00
2016-05-20 17:22:10 +00:00
// 'attach' subcommand.
attachCommand := & cobra . Command {
2017-02-10 14:11:40 +00:00
Use : "attach pid [executable]" ,
2016-05-20 17:22:10 +00:00
Short : "Attach to running process and begin debugging." ,
2016-05-20 17:51:23 +00:00
Long : ` Attach to an already running process and begin debugging it .
This command will cause Delve to take control of an already running process , and
begin a new debug session . When exiting the debug session you will have the
option to let the process continue or kill it .
` ,
2016-05-20 17:22:10 +00:00
PersistentPreRunE : func ( cmd * cobra . Command , args [ ] string ) error {
2023-08-09 17:30:22 +00:00
if len ( args ) == 0 && attachWaitFor == "" {
2016-05-20 17:22:10 +00:00
return errors . New ( "you must provide a PID" )
}
return nil
2016-02-19 18:32:24 +00:00
} ,
2016-05-20 17:22:10 +00:00
Run : attachCmd ,
2024-04-18 15:57:33 +00:00
ValidArgsFunction : func ( cmd * cobra . Command , args [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
if len ( args ) != 1 {
return nil , cobra . ShellCompDirectiveNoFileComp
}
return nil , cobra . ShellCompDirectiveDefault
} ,
2016-02-19 18:32:24 +00:00
}
2020-04-10 01:51:00 +00:00
attachCommand . Flags ( ) . BoolVar ( & continueOnStart , "continue" , false , "Continue the debugged process on start." )
2023-08-09 17:30:22 +00:00
attachCommand . Flags ( ) . StringVar ( & attachWaitFor , "waitfor" , "" , "Wait for a process with a name beginning with this prefix" )
2024-04-09 18:04:55 +00:00
must ( attachCommand . RegisterFlagCompletionFunc ( "waitfor" , cobra . NoFileCompletions ) )
2023-08-09 17:30:22 +00:00
attachCommand . Flags ( ) . Float64Var ( & attachWaitForInterval , "waitfor-interval" , 1 , "Interval between checks of the process list, in millisecond" )
2024-04-09 18:04:55 +00:00
must ( attachCommand . RegisterFlagCompletionFunc ( "waitfor-interval" , cobra . NoFileCompletions ) )
2023-08-09 17:30:22 +00:00
attachCommand . Flags ( ) . Float64Var ( & attachWaitForDuration , "waitfor-duration" , 0 , "Total time to wait for a process" )
2024-04-09 18:04:55 +00:00
must ( attachCommand . RegisterFlagCompletionFunc ( "waitfor-duration" , cobra . NoFileCompletions ) )
2020-03-31 22:10:37 +00:00
rootCommand . AddCommand ( attachCommand )
2016-02-19 18:32:24 +00:00
2016-05-20 17:22:10 +00:00
// 'connect' subcommand.
connectCommand := & cobra . Command {
Use : "connect addr" ,
2022-01-06 16:57:40 +00:00
Short : "Connect to a headless debug server with a terminal client." ,
2024-02-07 19:04:45 +00:00
Long : "Connect to a running headless debug server with a terminal client. Prefix with 'unix:' to use a unix domain socket." ,
2016-05-20 17:22:10 +00:00
PersistentPreRunE : func ( cmd * cobra . Command , args [ ] string ) error {
if len ( args ) == 0 {
return errors . New ( "you must provide an address as the first argument" )
}
return nil
2016-02-19 18:32:24 +00:00
} ,
2024-04-18 15:57:33 +00:00
Run : connectCmd ,
ValidArgsFunction : cobra . NoFileCompletions ,
2016-02-19 18:32:24 +00:00
}
2020-03-31 22:10:37 +00:00
rootCommand . AddCommand ( connectCommand )
2016-02-19 18:32:24 +00:00
2020-02-15 19:52:53 +00:00
// 'dap' subcommand.
dapCommand := & cobra . Command {
Use : "dap" ,
2022-01-06 16:57:40 +00:00
Short : "Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP)." ,
Long : ` Starts a headless TCP server communicating via Debug Adaptor Protocol ( DAP ) .
The server is always headless and requires a DAP client like VS Code to connect and request a binary
to be launched or a process to be attached to . The following modes can be specified via the client ' s launch config :
- launch + exec ( executes precompiled binary , like ' dlv exec ' )
- launch + debug ( builds and launches , like ' dlv debug ' )
- launch + test ( builds and tests , like ' dlv test ' )
2021-07-21 14:38:04 +00:00
- launch + replay ( replays an rr trace , like ' dlv replay ' )
2022-01-06 16:57:40 +00:00
- launch + core ( replays a core dump file , like ' dlv core ' )
- attach + local ( attaches to a running process , like ' dlv attach ' )
2021-08-23 18:27:49 +00:00
Program and output binary paths will be interpreted relative to dlv ' s working directory .
2022-01-06 16:57:40 +00:00
This server does not accept multiple client connections ( -- accept - multiclient ) .
Use ' dlv [ command ] -- headless ' instead and a DAP client with attach + remote config .
2021-06-29 13:15:47 +00:00
While -- continue is not supported , stopOnEntry launch / attach attribute can be used to control if
2021-10-13 18:43:47 +00:00
execution is resumed at the start of the debug session .
The -- client - addr flag is a special flag that makes the server initiate a debug session
by dialing in to the host : port where a DAP client is waiting . This server process
will exit when the debug session ends . ` ,
2024-04-18 15:57:33 +00:00
Run : dapCmd ,
ValidArgsFunction : cobra . NoFileCompletions ,
2020-02-15 19:52:53 +00:00
}
2024-09-30 18:20:48 +00:00
dapCommand . Flags ( ) . StringVar ( & dapClientAddr , "client-addr" , "" , "Address where the DAP client is waiting for the DAP server to dial in. Prefix with 'unix:' to use a unix domain socket." )
2024-04-09 18:04:55 +00:00
must ( dapCommand . RegisterFlagCompletionFunc ( "client-addr" , cobra . NoFileCompletions ) )
2021-10-13 18:43:47 +00:00
2021-07-16 16:49:16 +00:00
// TODO(polina): support --tty when dlv dap allows to launch a program from command-line
2020-03-31 22:10:37 +00:00
rootCommand . AddCommand ( dapCommand )
2020-02-15 19:52:53 +00:00
2016-02-19 18:32:24 +00:00
// 'debug' subcommand.
debugCommand := & cobra . Command {
Use : "debug [package]" ,
2016-05-25 19:13:16 +00:00
Short : "Compile and begin debugging main package in current directory, or the package specified." ,
2016-05-20 17:51:23 +00:00
Long : ` Compiles your program with optimizations disabled , starts and attaches to it .
By default , with no arguments , Delve will compile the ' main ' package in the
current directory , and begin to debug it . Alternatively you can specify a
package name and Delve will compile that package instead , and begin a new debug
session . ` ,
2024-04-18 15:57:33 +00:00
Run : debugCmd ,
ValidArgsFunction : cobra . NoFileCompletions ,
2016-02-19 18:32:24 +00:00
}
2023-05-16 16:36:15 +00:00
debugCommand . Flags ( ) . String ( "output" , "" , "Output path for the binary." )
2024-04-09 18:04:55 +00:00
must ( debugCommand . MarkFlagFilename ( "output" ) )
2020-03-31 22:10:37 +00:00
debugCommand . Flags ( ) . BoolVar ( & continueOnStart , "continue" , false , "Continue the debugged process on start." )
2020-04-04 22:19:35 +00:00
debugCommand . Flags ( ) . StringVar ( & tty , "tty" , "" , "TTY to use for the target program" )
2024-04-09 18:04:55 +00:00
must ( debugCommand . MarkFlagFilename ( "tty" ) )
2020-03-31 22:10:37 +00:00
rootCommand . AddCommand ( debugCommand )
2016-02-19 18:32:24 +00:00
// 'exec' subcommand.
execCommand := & cobra . Command {
2017-06-20 17:36:07 +00:00
Use : "exec <path/to/binary>" ,
2016-05-20 17:51:23 +00:00
Short : "Execute a precompiled binary, and begin a debug session." ,
Long : ` Execute a precompiled binary and begin a debug session .
This command will cause Delve to exec the binary and immediately attach to it to
begin a new debug session . Please note that if the binary was not compiled with
optimizations disabled , it may be difficult to properly debug it . Please
2018-10-15 11:00:22 +00:00
consider compiling debugging binaries with - gcflags = "all=-N -l" on Go 1.10
or later , - gcflags = "-N -l" on earlier versions of Go . ` ,
2016-02-19 18:32:24 +00:00
PersistentPreRunE : func ( cmd * cobra . Command , args [ ] string ) error {
if len ( args ) == 0 {
return errors . New ( "you must provide a path to a binary" )
}
return nil
} ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
2020-06-05 18:03:09 +00:00
os . Exit ( execute ( 0 , args , conf , "" , debugger . ExecutingExistingFile , args , buildFlags ) )
2016-02-19 18:32:24 +00:00
} ,
2024-04-18 15:57:33 +00:00
ValidArgsFunction : func ( cmd * cobra . Command , args [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
if len ( args ) != 0 {
return nil , cobra . ShellCompDirectiveNoFileComp
}
return nil , cobra . ShellCompDirectiveDefault
} ,
2016-02-19 18:32:24 +00:00
}
2020-04-04 22:19:35 +00:00
execCommand . Flags ( ) . StringVar ( & tty , "tty" , "" , "TTY to use for the target program" )
2024-04-09 18:04:55 +00:00
must ( execCommand . MarkFlagFilename ( "tty" ) )
2020-03-31 22:10:37 +00:00
execCommand . Flags ( ) . BoolVar ( & continueOnStart , "continue" , false , "Continue the debugged process on start." )
rootCommand . AddCommand ( execCommand )
2016-02-19 18:32:24 +00:00
2016-05-20 17:22:10 +00:00
// Deprecated 'run' subcommand.
runCommand := & cobra . Command {
Use : "run" ,
Short : "Deprecated command. Use 'debug' instead." ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
fmt . Println ( "This command is deprecated, please use 'debug' instead." )
os . Exit ( 0 )
} ,
2023-08-09 17:37:55 +00:00
Hidden : true ,
2016-02-19 18:32:24 +00:00
}
2020-03-31 22:10:37 +00:00
rootCommand . AddCommand ( runCommand )
2016-02-19 18:32:24 +00:00
// 'test' subcommand.
testCommand := & cobra . Command {
Use : "test [package]" ,
Short : "Compile test binary and begin debugging program." ,
2016-05-20 17:51:23 +00:00
Long : ` Compiles a test binary with optimizations disabled and begins a new debug session .
The test command allows you to begin a new debug session in the context of your
unit tests . By default Delve will debug the tests in the current directory .
Alternatively you can specify a package name , and Delve will debug the tests in
2021-05-04 19:37:09 +00:00
that package instead . Double - dashes ` + " ` -- ` " + ` can be used to pass arguments to the test program :
2023-02-28 13:52:52 +00:00
dlv test [ package ] -- - test . run TestSomething - test . v - other - argument
2021-05-04 19:37:09 +00:00
See also : ' go help testflag ' . ` ,
2024-04-18 15:57:33 +00:00
Run : testCmd ,
ValidArgsFunction : cobra . NoFileCompletions ,
2016-02-19 18:32:24 +00:00
}
2023-05-16 16:36:15 +00:00
testCommand . Flags ( ) . String ( "output" , "" , "Output path for the binary." )
2024-04-09 18:04:55 +00:00
must ( testCommand . MarkFlagFilename ( "output" ) )
2020-03-31 22:10:37 +00:00
rootCommand . AddCommand ( testCommand )
2016-02-19 18:32:24 +00:00
2016-05-20 17:22:10 +00:00
// 'trace' subcommand.
traceCommand := & cobra . Command {
Use : "trace [package] regexp" ,
Short : "Compile and begin tracing program." ,
2016-05-20 17:51:23 +00:00
Long : ` Trace program execution .
The trace sub command will set a tracepoint on every function matching the
provided regular expression and output information when tracepoint is hit . This
is useful if you do not want to begin an entire debug session , but merely want
2020-05-13 06:38:10 +00:00
to know what functions your process is executing .
The output of the trace sub command is printed to stderr , so if you would like to
only see the output of the trace operations you can redirect stdout . ` ,
2023-06-06 23:12:11 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2023-08-09 17:30:22 +00:00
os . Exit ( traceCmd ( cmd , args , conf ) )
} ,
2024-04-18 15:57:33 +00:00
ValidArgsFunction : cobra . NoFileCompletions ,
2016-02-19 18:32:24 +00:00
}
2016-05-20 17:22:10 +00:00
traceCommand . Flags ( ) . IntVarP ( & traceAttachPid , "pid" , "p" , 0 , "Pid to attach to." )
2024-04-09 18:04:55 +00:00
must ( traceCommand . RegisterFlagCompletionFunc ( "pid" , cobra . NoFileCompletions ) )
2018-10-17 22:06:18 +00:00
traceCommand . Flags ( ) . StringVarP ( & traceExecFile , "exec" , "e" , "" , "Binary file to exec and trace." )
2024-04-09 18:04:55 +00:00
must ( traceCommand . MarkFlagFilename ( "exec" ) )
2018-10-17 22:06:18 +00:00
traceCommand . Flags ( ) . BoolVarP ( & traceTestBinary , "test" , "t" , false , "Trace a test binary." )
2021-07-31 15:16:26 +00:00
traceCommand . Flags ( ) . BoolVarP ( & traceUseEBPF , "ebpf" , "" , false , "Trace using eBPF (experimental)." )
2023-05-08 17:41:47 +00:00
traceCommand . Flags ( ) . BoolVarP ( & traceShowTimestamp , "timestamp" , "" , false , "Show timestamp in the output" )
2022-07-22 15:57:57 +00:00
traceCommand . Flags ( ) . IntVarP ( & traceStackDepth , "stack" , "s" , 0 , "Show stack trace with given depth. (Ignored with --ebpf)" )
2024-04-09 18:04:55 +00:00
must ( traceCommand . RegisterFlagCompletionFunc ( "stack" , cobra . NoFileCompletions ) )
2023-05-16 16:36:15 +00:00
traceCommand . Flags ( ) . String ( "output" , "" , "Output path for the binary." )
2024-04-09 18:04:55 +00:00
must ( traceCommand . MarkFlagFilename ( "output" ) )
2024-06-12 19:35:48 +00:00
traceCommand . Flags ( ) . IntVarP ( & traceFollowCalls , "follow-calls" , "" , 0 , "Trace all children of the function to the required depth" )
2020-03-31 22:10:37 +00:00
rootCommand . AddCommand ( traceCommand )
2016-02-19 18:32:24 +00:00
2017-04-03 09:17:54 +00:00
coreCommand := & cobra . Command {
Use : "core <executable> <core>" ,
Short : "Examine a core dump." ,
2020-05-04 17:00:33 +00:00
Long : ` Examine a core dump ( only supports linux and windows core dumps ) .
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
2017-04-03 09:17:54 +00:00
The core command will open the specified core file and the associated
executable and let you examine the state of the process when the
2018-10-16 10:48:59 +00:00
core dump was taken .
2021-01-29 21:39:33 +00:00
Currently supports linux / amd64 and linux / arm64 core files , windows / amd64 minidumps and core files generated by Delve ' s ' dump ' command . ` ,
2017-04-03 09:17:54 +00:00
PersistentPreRunE : func ( cmd * cobra . Command , args [ ] string ) error {
if len ( args ) != 2 {
return errors . New ( "you must provide a core file and an executable" )
}
return nil
} ,
Run : coreCmd ,
2024-04-18 15:57:33 +00:00
ValidArgsFunction : func ( cmd * cobra . Command , args [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
if len ( args ) > 2 {
return nil , cobra . ShellCompDirectiveNoFileComp
}
return nil , cobra . ShellCompDirectiveDefault
} ,
2017-04-03 09:17:54 +00:00
}
2022-11-17 08:13:25 +00:00
// -c is unused and exists so delve can be used with coredumpctl
core := false
coreCommand . Flags ( ) . BoolVarP ( & core , "core" , "c" , false , "" )
coreCommand . Flags ( ) . MarkHidden ( "core" )
2020-03-31 22:10:37 +00:00
rootCommand . AddCommand ( coreCommand )
2017-04-03 09:17:54 +00:00
2016-05-20 17:22:10 +00:00
// 'version' subcommand.
2021-07-27 16:38:48 +00:00
var versionVerbose = false
2016-05-20 17:22:10 +00:00
versionCommand := & cobra . Command {
Use : "version" ,
Short : "Prints version." ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
fmt . Printf ( "Delve Debugger\n%s\n" , version . DelveVersion )
2021-07-27 16:38:48 +00:00
if versionVerbose {
fmt . Printf ( "Build Details: %s\n" , version . BuildInfo ( ) )
}
2016-02-19 18:32:24 +00:00
} ,
2024-04-18 15:57:33 +00:00
ValidArgsFunction : cobra . NoFileCompletions ,
2016-02-19 18:32:24 +00:00
}
2021-07-27 16:38:48 +00:00
versionCommand . Flags ( ) . BoolVarP ( & versionVerbose , "verbose" , "v" , false , "print verbose version info" )
2020-03-31 22:10:37 +00:00
rootCommand . AddCommand ( versionCommand )
2016-02-19 18:32:24 +00:00
2017-06-20 17:36:07 +00:00
if path , _ := exec . LookPath ( "rr" ) ; path != "" || docCall {
2017-05-05 22:17:52 +00:00
replayCommand := & cobra . Command {
Use : "replay [trace directory]" ,
Short : "Replays a rr trace." ,
Long : ` Replays a rr trace .
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
2017-05-05 22:17:52 +00:00
The replay command will open a trace generated by mozilla rr . Mozilla rr must be installed :
https : //github.com/mozilla/rr
` ,
PersistentPreRunE : func ( cmd * cobra . Command , args [ ] string ) error {
if len ( args ) == 0 {
return errors . New ( "you must provide a path to a binary" )
}
return nil
} ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
2020-03-31 22:10:37 +00:00
backend = "rr"
2020-06-05 18:03:09 +00:00
os . Exit ( execute ( 0 , [ ] string { } , conf , args [ 0 ] , debugger . ExecutingOther , args , buildFlags ) )
2017-05-05 22:17:52 +00:00
} ,
2024-04-18 15:57:33 +00:00
ValidArgsFunction : func ( cmd * cobra . Command , args [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
if len ( args ) > 2 {
return nil , cobra . ShellCompDirectiveNoFileComp
}
return nil , cobra . ShellCompDirectiveDefault
} ,
2017-05-05 22:17:52 +00:00
}
2023-03-01 19:28:32 +00:00
replayCommand . Flags ( ) . IntVarP ( & rrOnProcessPid , "onprocess" , "p" , 0 ,
"Pass onprocess pid to rr." )
2024-04-09 18:04:55 +00:00
must ( replayCommand . RegisterFlagCompletionFunc ( "onprocess" , cobra . NoFileCompletions ) )
2023-03-01 19:28:32 +00:00
2020-03-31 22:10:37 +00:00
rootCommand . AddCommand ( replayCommand )
2017-05-05 22:17:52 +00:00
}
2020-03-31 22:10:37 +00:00
rootCommand . AddCommand ( & cobra . Command {
2019-08-01 23:28:38 +00:00
Use : "backend" ,
Short : "Help about the --backend flag." ,
Long : ` The -- backend flag specifies which backend should be used , possible values
are :
default Uses lldb on macOS , native everywhere else .
native Native backend .
lldb Uses lldb - server or debugserver .
rr Uses mozilla rr ( https : //github.com/mozilla/rr).
2024-05-24 18:33:23 +00:00
Some backends can be configured using environment variables :
* DELVE_DEBUGSERVER_PATH specifies the path of the debugserver executable for the lldb backend
* DELVE_RR_RECORD_FLAGS specifies additional flags used when calling ' rr record '
* DELVE_RR_REPLAY_FLAGS specifies additional flags used when calling ' rr replay '
2024-09-30 18:20:48 +00:00
` ,
} )
2019-08-01 23:28:38 +00:00
2020-03-31 22:10:37 +00:00
rootCommand . AddCommand ( & cobra . Command {
2019-08-01 23:28:38 +00:00
Use : "log" ,
Short : "Help about logging flags." ,
Long : ` Logging can be enabled by specifying the -- log flag and using the
-- log - output flag to select which components should produce logs .
The argument of -- log - output must be a comma separated list of component
names selected from this list :
debugger Log debugger commands
gdbwire Log connection to gdbserial backend
lldbout Copy output from debugserver / lldb to standard output
debuglineerr Log recoverable errors reading . debug_line
rpc Log all RPC messages
2020-02-15 19:52:53 +00:00
dap Log all DAP messages
2019-08-01 23:28:38 +00:00
fncall Log function call protocol
minidump Log minidump loading
2023-06-27 16:33:07 +00:00
stack Log stacktracer
2019-08-01 23:28:38 +00:00
Additionally -- log - dest can be used to specify where the logs should be
written .
If the argument is a number it will be interpreted as a file descriptor ,
otherwise as a file path .
2020-02-15 19:52:53 +00:00
This option will also redirect the "server listening at" message in headless
and dap modes .
2019-08-01 23:28:38 +00:00
2020-08-21 14:14:02 +00:00
` ,
} )
rootCommand . AddCommand ( & cobra . Command {
Use : "redirect" ,
Short : "Help about file redirection." ,
Long : ` The standard file descriptors of the target process can be controlled using the ' - r ' and ' -- tty ' arguments .
The -- tty argument allows redirecting all standard descriptors to a terminal , specified as an argument to -- tty .
The syntax for ' - r ' argument is :
- r [ source : ] destination
Where source is one of ' stdin ' , ' stdout ' or ' stderr ' and destination is the path to a file . If the source is omitted stdin is used implicitly .
File redirects can also be changed using the ' restart ' command .
2019-08-01 23:28:38 +00:00
` ,
} )
2024-10-31 17:19:08 +00:00
rootCommand . AddCommand ( & cobra . Command {
Use : "substitute-path-guess-helper" ,
Hidden : true ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
gsp , err := rpc2 . MakeGuessSusbtitutePathIn ( )
if err != nil {
fmt . Printf ( "ERROR: %v\n" , err )
os . Exit ( 1 )
}
err = json . NewEncoder ( os . Stdout ) . Encode ( gsp )
if err != nil {
fmt . Printf ( "ERROR: %v\n" , err )
os . Exit ( 1 )
}
os . Exit ( 0 )
} ,
} )
2020-03-31 22:10:37 +00:00
rootCommand . DisableAutoGenTag = true
2018-04-18 07:36:40 +00:00
2023-08-09 17:37:55 +00:00
configUsageFunc ( rootCommand )
2020-03-31 22:10:37 +00:00
return rootCommand
2016-02-19 18:32:24 +00:00
}
2020-02-15 19:52:53 +00:00
func dapCmd ( cmd * cobra . Command , args [ ] string ) {
status := func ( ) int {
2024-04-09 18:04:55 +00:00
if err := logflags . Setup ( logFlag , logOutput , logDest ) ; err != nil {
2020-02-15 19:52:53 +00:00
fmt . Fprintf ( os . Stderr , "%v\n" , err )
return 1
}
defer logflags . Close ( )
2021-10-14 01:46:20 +00:00
if loadConfErr != nil {
logflags . DebuggerLogger ( ) . Errorf ( "%v" , loadConfErr )
}
2021-07-16 16:49:16 +00:00
if cmd . Flag ( "headless" ) . Changed {
2021-01-13 12:10:15 +00:00
fmt . Fprintf ( os . Stderr , "Warning: dap mode is always headless\n" )
2020-02-15 19:52:53 +00:00
}
2020-03-31 22:10:37 +00:00
if acceptMulti {
2021-01-13 12:10:15 +00:00
fmt . Fprintf ( os . Stderr , "Warning: accept-multiclient mode not supported with dap\n" )
2020-02-15 19:52:53 +00:00
}
2020-03-31 22:10:37 +00:00
if initFile != "" {
2020-02-15 19:52:53 +00:00
fmt . Fprint ( os . Stderr , "Warning: init file ignored with dap\n" )
}
2020-03-31 22:10:37 +00:00
if continueOnStart {
2020-02-15 19:52:53 +00:00
fmt . Fprintf ( os . Stderr , "Warning: continue ignored with dap; specify via launch/attach request instead\n" )
}
2021-07-18 09:37:41 +00:00
if backend != "default" {
fmt . Fprintf ( os . Stderr , "Warning: backend ignored with dap; specify via launch/attach request instead\n" )
}
2020-03-31 22:10:37 +00:00
if buildFlags != "" {
2020-02-15 19:52:53 +00:00
fmt . Fprintf ( os . Stderr , "Warning: build flags ignored with dap; specify via launch/attach request instead\n" )
}
2020-03-31 22:10:37 +00:00
if workingDir != "" {
2021-03-23 03:06:09 +00:00
fmt . Fprintf ( os . Stderr , "Warning: working directory ignored with dap: specify via launch request instead\n" )
2020-02-15 19:52:53 +00:00
}
dlvArgs , targetArgs := splitArgs ( cmd , args )
if len ( dlvArgs ) > 0 {
fmt . Fprintf ( os . Stderr , "Warning: debug arguments ignored with dap; specify via launch/attach request instead\n" )
}
if len ( targetArgs ) > 0 {
fmt . Fprintf ( os . Stderr , "Warning: program flags ignored with dap; specify via launch/attach request instead\n" )
}
disconnectChan := make ( chan struct { } )
2023-11-03 16:22:02 +00:00
cfg := & service . Config {
2020-04-13 18:07:15 +00:00
DisconnectChan : disconnectChan ,
Debugger : debugger . Config {
Backend : backend ,
2021-07-16 16:49:16 +00:00
Foreground : true , // server always runs without terminal client
2020-04-13 18:07:15 +00:00
DebugInfoDirectories : conf . DebugInfoDirectories ,
CheckGoVersion : checkGoVersion ,
2022-04-12 21:20:22 +00:00
DisableASLR : disableASLR ,
2020-04-13 18:07:15 +00:00
} ,
2021-05-19 17:29:05 +00:00
CheckLocalConnUser : checkLocalConnUser ,
2021-10-13 18:43:47 +00:00
}
var conn net . Conn
if dapClientAddr == "" {
2024-02-07 19:04:45 +00:00
listener , err := netListen ( addr )
2021-10-13 18:43:47 +00:00
if err != nil {
fmt . Printf ( "couldn't start listener: %s\n" , err )
return 1
}
2023-11-03 16:22:02 +00:00
cfg . Listener = listener
2021-10-13 18:43:47 +00:00
} else { // with a predetermined client.
2024-09-30 18:20:48 +00:00
conn = netDial ( dapClientAddr )
2021-10-13 18:43:47 +00:00
}
2020-02-15 19:52:53 +00:00
2023-11-03 16:22:02 +00:00
server := dap . NewServer ( cfg )
2021-10-13 18:43:47 +00:00
defer server . Stop ( )
if conn == nil {
server . Run ( )
} else { // work with a predetermined client.
server . RunWithClient ( conn )
}
2020-02-15 19:52:53 +00:00
waitForDisconnectSignal ( disconnectChan )
return 0
} ( )
os . Exit ( status )
}
2021-08-03 16:48:01 +00:00
func buildBinary ( cmd * cobra . Command , args [ ] string , isTest bool ) ( string , bool ) {
2023-05-16 16:36:15 +00:00
outputFlag := cmd . Flag ( "output" ) . Value . String ( )
var debugname string
var err error
if outputFlag == "" {
if isTest {
debugname = gobuild . DefaultDebugBinaryPath ( "debug.test" )
} else {
debugname = gobuild . DefaultDebugBinaryPath ( "__debug_bin" )
}
} else {
debugname , err = filepath . Abs ( outputFlag )
if err != nil {
fmt . Fprintf ( os . Stderr , "%v\n" , err )
return "" , false
}
2021-08-03 16:48:01 +00:00
}
if isTest {
err = gobuild . GoTestBuild ( debugname , args , buildFlags )
} else {
err = gobuild . GoBuild ( debugname , args , buildFlags )
}
if err != nil {
2023-05-16 16:36:15 +00:00
if outputFlag == "" {
gobuild . Remove ( debugname )
}
2021-08-03 16:48:01 +00:00
fmt . Fprintf ( os . Stderr , "%v\n" , err )
return "" , false
}
return debugname , true
}
2016-02-19 18:32:24 +00:00
func debugCmd ( cmd * cobra . Command , args [ ] string ) {
status := func ( ) int {
dlvArgs , targetArgs := splitArgs ( cmd , args )
2021-08-03 16:48:01 +00:00
debugname , ok := buildBinary ( cmd , dlvArgs , false )
if ! ok {
2016-02-19 18:32:24 +00:00
return 1
}
2020-03-04 17:22:51 +00:00
defer gobuild . Remove ( debugname )
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
processArgs := append ( [ ] string { debugname } , targetArgs ... )
2020-06-05 18:03:09 +00:00
return execute ( 0 , processArgs , conf , "" , debugger . ExecutingGeneratedFile , dlvArgs , buildFlags )
2016-02-19 18:32:24 +00:00
} ( )
os . Exit ( status )
}
2023-06-06 23:12:11 +00:00
func traceCmd ( cmd * cobra . Command , args [ ] string , conf * config . Config ) int {
2016-02-19 18:32:24 +00:00
status := func ( ) int {
2024-04-09 18:04:55 +00:00
err := logflags . Setup ( logFlag , logOutput , logDest )
2019-03-27 21:58:36 +00:00
defer logflags . Close ( )
2018-10-17 22:06:18 +00:00
if err != nil {
2018-04-19 08:43:23 +00:00
fmt . Fprintf ( os . Stderr , "%v\n" , err )
return 1
}
2021-10-14 01:46:20 +00:00
if loadConfErr != nil {
logflags . DebuggerLogger ( ) . Errorf ( "%v" , loadConfErr )
}
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
2020-03-31 22:10:37 +00:00
if headless {
2018-09-03 08:00:30 +00:00
fmt . Fprintf ( os . Stderr , "Warning: headless mode not supported with trace\n" )
}
2020-03-31 22:10:37 +00:00
if acceptMulti {
2018-09-03 08:00:30 +00:00
fmt . Fprintf ( os . Stderr , "Warning: accept multiclient mode not supported with trace" )
}
2016-02-19 18:32:24 +00:00
var regexp string
var processArgs [ ] string
dlvArgs , targetArgs := splitArgs ( cmd , args )
2020-06-02 19:07:03 +00:00
var dlvArgsLen = len ( dlvArgs )
2022-01-29 12:18:24 +00:00
switch dlvArgsLen {
case 0 :
fmt . Fprintf ( os . Stderr , "you must supply a regexp for functions to trace\n" )
return 1
case 1 :
2020-06-02 19:07:03 +00:00
regexp = args [ 0 ]
dlvArgs = dlvArgs [ 0 : 0 ]
2022-01-29 12:18:24 +00:00
default :
2020-06-02 19:07:03 +00:00
regexp = dlvArgs [ dlvArgsLen - 1 ]
dlvArgs = dlvArgs [ : dlvArgsLen - 1 ]
}
2016-02-19 18:32:24 +00:00
2021-07-31 15:16:26 +00:00
var debugname string
2016-02-19 18:32:24 +00:00
if traceAttachPid == 0 {
2020-06-02 19:07:03 +00:00
if dlvArgsLen >= 2 && traceExecFile != "" {
2022-07-22 15:57:57 +00:00
fmt . Fprintln ( os . Stderr , "Cannot specify package when using --exec." )
2020-06-02 19:07:03 +00:00
return 1
2016-02-19 18:32:24 +00:00
}
2018-10-17 22:06:18 +00:00
2021-07-31 15:16:26 +00:00
debugname = traceExecFile
2018-10-17 22:06:18 +00:00
if traceExecFile == "" {
2021-08-03 16:48:01 +00:00
debugexe , ok := buildBinary ( cmd , dlvArgs , traceTestBinary )
if ! ok {
2018-10-17 22:06:18 +00:00
return 1
}
2021-08-03 16:48:01 +00:00
debugname = debugexe
2020-03-04 17:22:51 +00:00
defer gobuild . Remove ( debugname )
2016-02-19 18:32:24 +00:00
}
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
processArgs = append ( [ ] string { debugname } , targetArgs ... )
2016-02-19 18:32:24 +00:00
}
2024-06-12 19:35:48 +00:00
if dlvArgsLen >= 3 && traceFollowCalls <= 0 {
fmt . Fprintln ( os . Stderr , "Need to specify a trace depth of atleast 1" )
return 1
}
2018-09-03 08:00:30 +00:00
// Make a local in-memory connection that client and server use to communicate
listener , clientConn := service . ListenerPipe ( )
2016-02-19 18:32:24 +00:00
defer listener . Close ( )
2020-08-31 17:51:10 +00:00
if workingDir == "" {
workingDir = "."
}
2016-02-19 18:32:24 +00:00
// Create and start a debug server
2016-06-19 06:43:29 +00:00
server := rpccommon . NewServer ( & service . Config {
2020-04-13 18:07:15 +00:00
Listener : listener ,
ProcessArgs : processArgs ,
APIVersion : 2 ,
Debugger : debugger . Config {
2023-08-09 17:30:22 +00:00
AttachPid : traceAttachPid ,
WorkingDir : workingDir ,
Backend : backend ,
CheckGoVersion : checkGoVersion ,
2023-06-06 23:12:11 +00:00
DebugInfoDirectories : conf . DebugInfoDirectories ,
2020-04-13 18:07:15 +00:00
} ,
2018-06-14 18:12:11 +00:00
} )
2016-02-19 18:32:24 +00:00
if err := server . Run ( ) ; err != nil {
fmt . Fprintln ( os . Stderr , err )
return 1
}
2018-09-03 08:00:30 +00:00
client := rpc2 . NewClientFromConn ( clientConn )
2022-07-22 15:57:57 +00:00
defer client . Detach ( true )
2023-08-23 05:45:44 +00:00
ch := make ( chan os . Signal , 1 )
signal . Notify ( ch , syscall . SIGINT )
go func ( ) {
<- ch
client . Halt ( )
} ( )
2024-06-12 19:35:48 +00:00
funcs , err := client . ListFunctions ( regexp , traceFollowCalls )
2016-02-19 18:32:24 +00:00
if err != nil {
fmt . Fprintln ( os . Stderr , err )
return 1
}
2022-07-22 15:57:57 +00:00
success := false
2016-02-19 18:32:24 +00:00
for i := range funcs {
2021-07-31 15:16:26 +00:00
if traceUseEBPF {
err := client . CreateEBPFTracepoint ( funcs [ i ] )
if err != nil {
2022-01-29 12:18:24 +00:00
fmt . Fprintf ( os . Stderr , "unable to set tracepoint on function %s: %#v\n" , funcs [ i ] , err )
2022-07-22 15:57:57 +00:00
} else {
success = true
2021-07-31 15:16:26 +00:00
}
} else {
// Fall back to breakpoint based tracing if we get an error.
2024-06-12 19:35:48 +00:00
var stackdepth int
// Default size of stackdepth to trace function calls and descendants=20
stackdepth = traceStackDepth
if traceFollowCalls > 0 && stackdepth == 0 {
stackdepth = 20
}
2018-10-16 15:49:20 +00:00
_ , err = client . CreateBreakpoint ( & api . Breakpoint {
2024-06-12 19:35:48 +00:00
FunctionName : funcs [ i ] ,
Tracepoint : true ,
Line : - 1 ,
Stacktrace : stackdepth ,
LoadArgs : & terminal . ShortLoadConfig ,
TraceFollowCalls : traceFollowCalls ,
RootFuncName : regexp ,
2018-10-16 15:49:20 +00:00
} )
2024-06-12 19:35:48 +00:00
2020-05-13 06:38:10 +00:00
if err != nil && ! isBreakpointExistsErr ( err ) {
2022-01-29 12:18:24 +00:00
fmt . Fprintf ( os . Stderr , "unable to set tracepoint on function %s: %#v\n" , funcs [ i ] , err )
2022-07-22 15:57:57 +00:00
continue
} else {
success = true
2018-10-16 15:49:20 +00:00
}
2021-07-31 15:16:26 +00:00
addrs , err := client . FunctionReturnLocations ( funcs [ i ] )
if err != nil {
2022-01-29 12:18:24 +00:00
fmt . Fprintf ( os . Stderr , "unable to set tracepoint on function %s: %#v\n" , funcs [ i ] , err )
2022-07-22 15:57:57 +00:00
continue
2021-07-31 15:16:26 +00:00
}
for i := range addrs {
_ , err = client . CreateBreakpoint ( & api . Breakpoint {
2024-06-12 19:35:48 +00:00
Addr : addrs [ i ] ,
TraceReturn : true ,
Stacktrace : stackdepth ,
Line : - 1 ,
LoadArgs : & terminal . ShortLoadConfig ,
TraceFollowCalls : traceFollowCalls ,
RootFuncName : regexp ,
2021-07-31 15:16:26 +00:00
} )
if err != nil && ! isBreakpointExistsErr ( err ) {
2022-01-29 12:18:24 +00:00
fmt . Fprintf ( os . Stderr , "unable to set tracepoint on function %s: %#v\n" , funcs [ i ] , err )
2022-07-22 15:57:57 +00:00
} else {
success = true
2021-07-31 15:16:26 +00:00
}
}
2018-10-16 15:49:20 +00:00
}
2016-02-19 18:32:24 +00:00
}
2022-07-22 15:57:57 +00:00
if ! success {
fmt . Fprintln ( os . Stderr , "no breakpoints set" )
return 1
}
2016-02-19 18:32:24 +00:00
cmds := terminal . DebugCommands ( client )
2023-05-08 17:41:47 +00:00
cfg := & config . Config {
TraceShowTimestamp : traceShowTimestamp ,
}
t := terminal . New ( client , cfg )
2022-08-04 08:10:54 +00:00
t . SetTraceNonInteractive ( )
2022-01-27 21:18:25 +00:00
t . RedirectTo ( os . Stderr )
2016-02-19 18:32:24 +00:00
defer t . Close ( )
2021-07-31 15:16:26 +00:00
if traceUseEBPF {
done := make ( chan struct { } )
defer close ( done )
go func ( ) {
for {
select {
case <- done :
return
default :
tracepoints , err := client . GetBufferedTracepoints ( )
if err != nil {
panic ( err )
}
for _ , t := range tracepoints {
var params strings . Builder
for _ , p := range t . InputParams {
if params . Len ( ) > 0 {
params . WriteString ( ", " )
}
if p . Kind == reflect . String {
params . WriteString ( fmt . Sprintf ( "%q" , p . Value ) )
} else {
params . WriteString ( p . Value )
}
}
2023-05-08 17:41:47 +00:00
if traceShowTimestamp {
fmt . Fprintf ( os . Stderr , "%s " , time . Now ( ) . Format ( time . RFC3339Nano ) )
}
2022-07-29 10:00:32 +00:00
if t . IsRet {
2021-10-25 19:37:36 +00:00
for _ , p := range t . ReturnParams {
fmt . Fprintf ( os . Stderr , "=> %#v\n" , p . Value )
}
} else {
fmt . Fprintf ( os . Stderr , "> (%d) %s(%s)\n" , t . GoroutineID , t . FunctionName , params . String ( ) )
}
2021-07-31 15:16:26 +00:00
}
}
}
} ( )
}
2022-08-04 08:10:54 +00:00
err = cmds . Call ( "continue" , t )
if err != nil {
fmt . Fprintln ( os . Stderr , err )
2023-02-02 10:11:31 +00:00
if ! strings . Contains ( err . Error ( ) , "exited" ) {
return 1
}
2022-08-04 08:10:54 +00:00
}
2016-02-19 18:32:24 +00:00
return 0
} ( )
2023-06-06 23:12:11 +00:00
return status
2016-02-19 18:32:24 +00:00
}
2020-05-13 06:38:10 +00:00
func isBreakpointExistsErr ( err error ) bool {
return strings . Contains ( err . Error ( ) , "Breakpoint exists" )
}
2016-02-19 18:32:24 +00:00
func testCmd ( cmd * cobra . Command , args [ ] string ) {
status := func ( ) int {
dlvArgs , targetArgs := splitArgs ( cmd , args )
2021-08-03 16:48:01 +00:00
debugname , ok := buildBinary ( cmd , dlvArgs , true )
if ! ok {
2016-02-19 18:32:24 +00:00
return 1
}
2020-03-04 17:22:51 +00:00
defer gobuild . Remove ( debugname )
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
processArgs := append ( [ ] string { debugname } , targetArgs ... )
2016-02-19 18:32:24 +00:00
2020-08-31 17:51:10 +00:00
if workingDir == "" {
2023-01-04 18:47:27 +00:00
workingDir = getPackageDir ( dlvArgs )
2020-08-31 17:51:10 +00:00
}
2020-06-05 18:03:09 +00:00
return execute ( 0 , processArgs , conf , "" , debugger . ExecutingGeneratedTest , dlvArgs , buildFlags )
2016-02-19 18:32:24 +00:00
} ( )
os . Exit ( status )
}
2023-01-04 18:47:27 +00:00
func getPackageDir ( pkg [ ] string ) string {
args := [ ] string { "list" , "--json" }
args = append ( args , pkg ... )
out , err := exec . Command ( "go" , args ... ) . CombinedOutput ( )
2020-08-31 17:51:10 +00:00
if err != nil {
return "."
}
type listOut struct {
Dir string ` json:"Dir" `
}
var listout listOut
err = json . Unmarshal ( out , & listout )
if err != nil {
return "."
}
return listout . Dir
}
2023-11-03 16:22:02 +00:00
func attachCmd ( _ * cobra . Command , args [ ] string ) {
2023-08-09 17:30:22 +00:00
var pid int
if len ( args ) > 0 {
var err error
pid , err = strconv . Atoi ( args [ 0 ] )
if err != nil {
fmt . Fprintf ( os . Stderr , "Invalid pid: %s\n" , args [ 0 ] )
os . Exit ( 1 )
}
args = args [ 1 : ]
2016-02-19 18:32:24 +00:00
}
2023-08-09 17:30:22 +00:00
os . Exit ( execute ( pid , args , conf , "" , debugger . ExecutingOther , args , buildFlags ) )
2017-04-03 09:17:54 +00:00
}
2023-11-03 16:22:02 +00:00
func coreCmd ( _ * cobra . Command , args [ ] string ) {
2020-06-05 18:03:09 +00:00
os . Exit ( execute ( 0 , [ ] string { args [ 0 ] } , conf , args [ 1 ] , debugger . ExecutingOther , args , buildFlags ) )
2016-02-19 18:32:24 +00:00
}
2023-11-03 16:22:02 +00:00
func connectCmd ( _ * cobra . Command , args [ ] string ) {
2024-04-09 18:04:55 +00:00
if err := logflags . Setup ( logFlag , logOutput , logDest ) ; err != nil {
2021-10-14 01:46:20 +00:00
fmt . Fprintf ( os . Stderr , "%v\n" , err )
os . Exit ( 1 )
return
}
if loadConfErr != nil {
logflags . DebuggerLogger ( ) . Errorf ( "%v" , loadConfErr )
}
2016-02-19 18:32:24 +00:00
addr := args [ 0 ]
if addr == "" {
2017-06-29 18:15:59 +00:00
fmt . Fprint ( os . Stderr , "An empty address was provided. You must provide an address as the first argument.\n" )
2023-05-15 17:22:33 +00:00
logflags . Close ( )
2016-02-19 18:32:24 +00:00
os . Exit ( 1 )
}
2023-11-03 16:22:02 +00:00
ec := connect ( addr , nil , conf )
2023-05-15 17:22:33 +00:00
logflags . Close ( )
os . Exit ( ec )
2016-02-19 18:32:24 +00:00
}
2020-02-15 19:52:53 +00:00
// waitForDisconnectSignal is a blocking function that waits for either
2021-07-21 14:43:06 +00:00
// a SIGINT (Ctrl-C) or SIGTERM (kill -15) OS signal or for disconnectChan
// to be closed by the server when the client disconnects.
2020-02-15 19:52:53 +00:00
// Note that in headless mode, the debugged process is foregrounded
// (to have control of the tty for debugging interactive programs),
// so SIGINT gets sent to the debuggee and not to delve.
func waitForDisconnectSignal ( disconnectChan chan struct { } ) {
ch := make ( chan os . Signal , 1 )
2021-07-21 14:43:06 +00:00
signal . Notify ( ch , syscall . SIGINT , syscall . SIGTERM )
2020-02-15 19:52:53 +00:00
if runtime . GOOS == "windows" {
// On windows Ctrl-C sent to inferior process is delivered
// as SIGINT to delve. Ignore it instead of stopping the server
// in order to be able to debug signal handlers.
go func ( ) {
2021-08-09 17:08:12 +00:00
for range ch {
2020-02-15 19:52:53 +00:00
}
} ( )
2021-08-09 17:08:12 +00:00
<- disconnectChan
2020-02-15 19:52:53 +00:00
} else {
select {
case <- ch :
case <- disconnectChan :
}
}
}
2016-02-19 18:32:24 +00:00
func splitArgs ( cmd * cobra . Command , args [ ] string ) ( [ ] string , [ ] string ) {
if cmd . ArgsLenAtDash ( ) >= 0 {
return args [ : cmd . ArgsLenAtDash ( ) ] , args [ cmd . ArgsLenAtDash ( ) : ]
}
return args , [ ] string { }
}
2023-11-03 16:22:02 +00:00
func connect ( addr string , clientConn net . Conn , conf * config . Config ) int {
2016-02-19 18:32:24 +00:00
// Create and start a terminal - attach to running instance
2018-09-03 08:00:30 +00:00
var client * rpc2 . RPCClient
2024-02-07 19:04:45 +00:00
if clientConn == nil {
2024-04-11 05:12:42 +00:00
if clientConn = netDial ( addr ) ; clientConn == nil {
return 1 // already logged
}
2018-09-03 08:00:30 +00:00
}
2024-02-07 19:04:45 +00:00
client = rpc2 . NewClientFromConn ( clientConn )
2018-11-30 08:53:29 +00:00
if client . IsMulticlient ( ) {
state , _ := client . GetStateNonBlocking ( )
// The error return of GetState will usually be the ErrProcessExited,
// which we don't care about. If there are other errors they will show up
// later, here we are only concerned about stopping a running target so
// that we can initialize our connection.
if state != nil && state . Running {
_ , err := client . Halt ( )
if err != nil {
fmt . Fprintf ( os . Stderr , "could not halt: %v" , err )
return 1
}
}
}
2016-02-19 18:32:24 +00:00
term := terminal . New ( client , conf )
2020-03-31 22:10:37 +00:00
term . InitFile = initFile
2016-02-19 18:32:24 +00:00
status , err := term . Run ( )
if err != nil {
fmt . Println ( err )
}
return status
}
2020-06-05 18:03:09 +00:00
func execute ( attachPid int , processArgs [ ] string , conf * config . Config , coreFile string , kind debugger . ExecuteKind , dlvArgs [ ] string , buildFlags string ) int {
2024-04-09 18:04:55 +00:00
if err := logflags . Setup ( logFlag , logOutput , logDest ) ; err != nil {
2018-04-19 08:43:23 +00:00
fmt . Fprintf ( os . Stderr , "%v\n" , err )
return 1
}
2019-03-27 21:58:36 +00:00
defer logflags . Close ( )
2021-10-14 01:46:20 +00:00
if loadConfErr != nil {
logflags . DebuggerLogger ( ) . Errorf ( "%v" , loadConfErr )
}
2018-03-31 16:03:27 +00:00
2020-03-31 22:10:37 +00:00
if headless && ( initFile != "" ) {
2019-07-19 17:59:38 +00:00
fmt . Fprint ( os . Stderr , "Warning: init file ignored with --headless\n" )
}
2020-03-31 22:10:37 +00:00
if continueOnStart {
if ! headless {
2019-07-19 17:59:38 +00:00
fmt . Fprint ( os . Stderr , "Error: --continue only works with --headless; use an init file\n" )
return 1
}
2020-03-31 22:10:37 +00:00
if ! acceptMulti {
2019-07-19 17:59:38 +00:00
fmt . Fprint ( os . Stderr , "Error: --continue requires --accept-multiclient\n" )
return 1
}
2016-02-19 18:32:24 +00:00
}
2020-03-31 22:10:37 +00:00
if ! headless && acceptMulti {
2018-06-21 11:07:37 +00:00
fmt . Fprint ( os . Stderr , "Warning accept-multi: ignored\n" )
2020-03-31 22:10:37 +00:00
// acceptMulti won't work in normal (non-headless) mode because we always
2018-06-21 11:07:37 +00:00
// call server.Stop after the terminal client exits.
2020-03-31 22:10:37 +00:00
acceptMulti = false
2018-06-21 11:07:37 +00:00
}
2020-08-21 14:14:02 +00:00
if ! headless && ! allowNonTerminalInteractive {
for _ , f := range [ ] struct {
name string
file * os . File
} { { "Stdin" , os . Stdin } , { "Stdout" , os . Stdout } , { "Stderr" , os . Stderr } } {
if f . file == nil {
continue
}
if ! isatty . IsTerminal ( f . file . Fd ( ) ) {
fmt . Fprintf ( os . Stderr , "%s is not a terminal, use '-r' to specify redirects for the target process or --allow-non-terminal-interactive=true if you really want to specify a redirect for Delve\n" , f . name )
return 1
}
}
}
if len ( redirects ) > 0 && tty != "" {
fmt . Fprintf ( os . Stderr , "Can not use -r and --tty together\n" )
return 1
}
redirects , err := parseRedirects ( redirects )
if err != nil {
fmt . Fprintf ( os . Stderr , "%v\n" , err )
return 1
}
2018-09-03 08:00:30 +00:00
var listener net . Listener
var clientConn net . Conn
// Make a TCP listener
2020-03-31 22:10:37 +00:00
if headless {
2024-02-07 19:04:45 +00:00
listener , err = netListen ( addr )
2018-09-03 08:00:30 +00:00
} else {
listener , clientConn = service . ListenerPipe ( )
}
if err != nil {
fmt . Printf ( "couldn't start listener: %s\n" , err )
return 1
}
defer listener . Close ( )
2018-10-09 06:09:01 +00:00
var server service . Server
2016-04-18 19:20:20 +00:00
2017-06-26 18:45:13 +00:00
disconnectChan := make ( chan struct { } )
2020-08-31 17:51:10 +00:00
if workingDir == "" {
workingDir = "."
}
2016-02-19 18:32:24 +00:00
// Create and start a debugger server
2020-03-31 22:10:37 +00:00
switch apiVersion {
2016-06-19 06:43:29 +00:00
case 1 , 2 :
server = rpccommon . NewServer ( & service . Config {
2020-04-13 18:07:15 +00:00
Listener : listener ,
ProcessArgs : processArgs ,
AcceptMulti : acceptMulti ,
APIVersion : apiVersion ,
CheckLocalConnUser : checkLocalConnUser ,
DisconnectChan : disconnectChan ,
Debugger : debugger . Config {
2023-08-09 17:30:22 +00:00
AttachPid : attachPid ,
WorkingDir : workingDir ,
Backend : backend ,
CoreFile : coreFile ,
Foreground : headless && tty == "" ,
Packages : dlvArgs ,
BuildFlags : buildFlags ,
ExecuteKind : kind ,
DebugInfoDirectories : conf . DebugInfoDirectories ,
CheckGoVersion : checkGoVersion ,
TTY : tty ,
Stdin : redirects [ 0 ] ,
Stdout : proc . OutputRedirect { Path : redirects [ 1 ] } ,
Stderr : proc . OutputRedirect { Path : redirects [ 2 ] } ,
DisableASLR : disableASLR ,
RrOnProcessPid : rrOnProcessPid ,
AttachWaitFor : attachWaitFor ,
AttachWaitForInterval : attachWaitForInterval ,
AttachWaitForDuration : attachWaitForDuration ,
2020-04-13 18:07:15 +00:00
} ,
2018-06-14 18:12:11 +00:00
} )
2016-04-18 19:20:20 +00:00
default :
2020-03-31 22:10:37 +00:00
fmt . Printf ( "Unknown API version: %d\n" , apiVersion )
2016-04-18 19:20:20 +00:00
return 1
}
2016-02-19 18:32:24 +00:00
if err := server . Run ( ) ; err != nil {
2023-11-03 16:22:02 +00:00
if errors . Is ( err , api . ErrNotExecutable ) {
2016-04-21 10:19:21 +00:00
switch kind {
2020-06-05 18:03:09 +00:00
case debugger . ExecutingGeneratedFile :
2016-04-21 10:19:21 +00:00
fmt . Fprintln ( os . Stderr , "Can not debug non-main package" )
return 1
2020-06-05 18:03:09 +00:00
case debugger . ExecutingExistingFile :
2016-04-21 10:19:21 +00:00
fmt . Fprintf ( os . Stderr , "%s is not executable\n" , processArgs [ 0 ] )
return 1
default :
// fallthrough
}
}
2016-02-19 18:32:24 +00:00
fmt . Fprintln ( os . Stderr , err )
return 1
}
var status int
2020-03-31 22:10:37 +00:00
if headless {
if continueOnStart {
2024-02-13 18:14:38 +00:00
addr := listener . Addr ( ) . String ( )
if _ , isuds := listener . ( * net . UnixListener ) ; isuds {
addr = "unix:" + addr
}
client := rpc2 . NewClientFromConn ( netDial ( addr ) )
2019-07-19 17:59:38 +00:00
client . Disconnect ( true ) // true = continue after disconnect
}
2020-02-15 19:52:53 +00:00
waitForDisconnectSignal ( disconnectChan )
2018-06-21 11:07:37 +00:00
err = server . Stop ( )
2018-08-13 08:00:54 +00:00
if err != nil {
fmt . Println ( err )
2017-07-26 18:53:43 +00:00
}
2016-02-19 18:32:24 +00:00
2018-08-13 08:00:54 +00:00
return status
2016-02-19 18:32:24 +00:00
}
2023-11-03 16:22:02 +00:00
return connect ( listener . Addr ( ) . String ( ) , clientConn , conf )
2016-02-19 18:32:24 +00:00
}
2020-08-21 14:14:02 +00:00
func parseRedirects ( redirects [ ] string ) ( [ 3 ] string , error ) {
r := [ 3 ] string { }
names := [ 3 ] string { "stdin" , "stdout" , "stderr" }
for _ , redirect := range redirects {
idx := 0
for i , name := range names {
pfx := name + ":"
if strings . HasPrefix ( redirect , pfx ) {
idx = i
redirect = redirect [ len ( pfx ) : ]
break
}
}
if r [ idx ] != "" {
return r , fmt . Errorf ( "redirect error: %s redirected twice" , names [ idx ] )
}
r [ idx ] = redirect
}
return r , nil
}
2023-08-09 17:37:55 +00:00
func configUsageFunc ( cmd * cobra . Command ) {
for _ , subcmd := range cmd . Commands ( ) {
configUsageFunc ( subcmd )
}
if cmd . Run == nil && cmd . Name ( ) != "dlv" {
return
}
usage := cmd . UsageFunc ( )
cmd . SetUsageFunc ( func ( cmd * cobra . Command ) error {
helphelpers . Prepare ( cmd )
return usage ( cmd )
} )
}
2024-02-07 19:04:45 +00:00
const unixAddrPrefix = "unix:"
func netListen ( addr string ) ( net . Listener , error ) {
if strings . HasPrefix ( addr , unixAddrPrefix ) {
return net . Listen ( "unix" , addr [ len ( unixAddrPrefix ) : ] )
}
return net . Listen ( "tcp" , addr )
}
func netDial ( addr string ) net . Conn {
var conn net . Conn
var err error
if strings . HasPrefix ( addr , unixAddrPrefix ) {
conn , err = net . Dial ( "unix" , addr [ len ( unixAddrPrefix ) : ] )
} else {
conn , err = net . Dial ( "tcp" , addr )
}
if err != nil {
logflags . RPCLogger ( ) . Errorf ( "error dialing %s: %v" , addr , err )
}
return conn
}
2024-04-09 18:04:55 +00:00
func must ( err error ) {
if err != nil {
log . Fatal ( err )
}
}