mirror of
https://github.com/go-delve/delve.git
synced 2025-10-28 04:35:19 +08:00
cmd/dlv: add --continue to continue process on launch/attach (#1585)
* Add --continue to continue process on launch/attach * Add small test of --continue * regenerate usage docs * minor cleanup * Use similar approach to `trace` and connect and detach using a client instance * back out previous attempt * regen usage doc * fix up continue test * fix TestContinue to properly test --continue * back out unnecessary changes * update faq
This commit is contained in:
committed by
Derek Parker
parent
f67239f302
commit
cb65877297
@ -28,14 +28,10 @@ And then connect to it from outside the container:
|
|||||||
dlv connect :4040
|
dlv connect :4040
|
||||||
```
|
```
|
||||||
|
|
||||||
The program will not start executing until you connect to Delve and send the `continue` command. If you want the program to start immediately you can do that by:
|
The program will not start executing until you connect to Delve and send the `continue` command. If you want the program to start immediately you can do that by passing the `--continue` and `--accept-multiclient` options to Delve:
|
||||||
|
|
||||||
1. Passing the `--accept-multiclient` option to the headless instance of delve: `dlv exec --headless --listen :4040 --accept-multiclient /path/to/executable`
|
|
||||||
2. Using the following script:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
#!/bin/bash
|
dlv exec --headless --continue --listen :4040 --accept-multiclient /path/to/executable
|
||||||
while true; do sleep 1; dlv connect :4040 --init <(echo quit -c) && exit; done
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the connection to Delve is unauthenticated and will allow arbitrary remote code execution: *do not do this in production*.
|
Note that the connection to Delve is unauthenticated and will allow arbitrary remote code execution: *do not do this in production*.
|
||||||
|
|||||||
@ -19,6 +19,7 @@ dlv debug [package]
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
|
--continue Continue the debugged process on start.
|
||||||
--output string Output path for the binary. (default "./__debug_bin")
|
--output string Output path for the binary. (default "./__debug_bin")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,12 @@ or later, -gcflags="-N -l" on earlier versions of Go.
|
|||||||
dlv exec <path/to/binary>
|
dlv exec <path/to/binary>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
--continue Continue the debugged process on start.
|
||||||
|
```
|
||||||
|
|
||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@ -33,6 +33,8 @@ var (
|
|||||||
LogDest string
|
LogDest string
|
||||||
// Headless is whether to run without terminal.
|
// Headless is whether to run without terminal.
|
||||||
Headless bool
|
Headless bool
|
||||||
|
// ContinueOnStart is whether to continue the process on startup
|
||||||
|
ContinueOnStart bool
|
||||||
// APIVersion is the requested API version while running headless
|
// APIVersion is the requested API version while running headless
|
||||||
APIVersion int
|
APIVersion int
|
||||||
// AcceptMulti allows multiple clients to connect to the same server
|
// AcceptMulti allows multiple clients to connect to the same server
|
||||||
@ -172,6 +174,7 @@ session.`,
|
|||||||
Run: debugCmd,
|
Run: debugCmd,
|
||||||
}
|
}
|
||||||
debugCommand.Flags().String("output", "./__debug_bin", "Output path for the binary.")
|
debugCommand.Flags().String("output", "./__debug_bin", "Output path for the binary.")
|
||||||
|
debugCommand.Flags().BoolVar(&ContinueOnStart, "continue", false, "Continue the debugged process on start.")
|
||||||
RootCommand.AddCommand(debugCommand)
|
RootCommand.AddCommand(debugCommand)
|
||||||
|
|
||||||
// 'exec' subcommand.
|
// 'exec' subcommand.
|
||||||
@ -195,6 +198,7 @@ or later, -gcflags="-N -l" on earlier versions of Go.`,
|
|||||||
os.Exit(execute(0, args, conf, "", executingExistingFile))
|
os.Exit(execute(0, args, conf, "", executingExistingFile))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
execCommand.Flags().BoolVar(&ContinueOnStart, "continue", false, "Continue the debugged process on start.")
|
||||||
RootCommand.AddCommand(execCommand)
|
RootCommand.AddCommand(execCommand)
|
||||||
|
|
||||||
// Deprecated 'run' subcommand.
|
// Deprecated 'run' subcommand.
|
||||||
@ -561,7 +565,17 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
|
|||||||
defer logflags.Close()
|
defer logflags.Close()
|
||||||
|
|
||||||
if Headless && (InitFile != "") {
|
if Headless && (InitFile != "") {
|
||||||
fmt.Fprint(os.Stderr, "Warning: init file ignored\n")
|
fmt.Fprint(os.Stderr, "Warning: init file ignored with --headless\n")
|
||||||
|
}
|
||||||
|
if ContinueOnStart {
|
||||||
|
if !Headless {
|
||||||
|
fmt.Fprint(os.Stderr, "Error: --continue only works with --headless; use an init file\n")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if !AcceptMulti {
|
||||||
|
fmt.Fprint(os.Stderr, "Error: --continue requires --accept-multiclient\n")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !Headless && AcceptMulti {
|
if !Headless && AcceptMulti {
|
||||||
@ -633,6 +647,11 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
|
|||||||
|
|
||||||
var status int
|
var status int
|
||||||
if Headless {
|
if Headless {
|
||||||
|
if ContinueOnStart {
|
||||||
|
var client *rpc2.RPCClient
|
||||||
|
client = rpc2.NewClient(listener.Addr().String())
|
||||||
|
client.Disconnect(true) // true = continue after disconnect
|
||||||
|
}
|
||||||
ch := make(chan os.Signal, 1)
|
ch := make(chan os.Signal, 1)
|
||||||
signal.Notify(ch, syscall.SIGINT)
|
signal.Notify(ch, syscall.SIGINT)
|
||||||
select {
|
select {
|
||||||
|
|||||||
@ -218,6 +218,39 @@ func TestOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestContinue verifies that the debugged executable starts immediately with --continue
|
||||||
|
func TestContinue(t *testing.T) {
|
||||||
|
const listenAddr = "localhost:40573"
|
||||||
|
|
||||||
|
dlvbin, tmpdir := getDlvBin(t)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
|
buildtestdir := filepath.Join(protest.FindFixturesDir(), "buildtest")
|
||||||
|
cmd := exec.Command(dlvbin, "debug", "--headless", "--continue", "--accept-multiclient", "--listen", listenAddr)
|
||||||
|
cmd.Dir = buildtestdir
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
assertNoError(err, t, "stderr pipe")
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
t.Fatalf("could not start headless instance: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
scan := bufio.NewScanner(stdout)
|
||||||
|
// wait for the debugger to start
|
||||||
|
for scan.Scan() {
|
||||||
|
t.Log(scan.Text())
|
||||||
|
if scan.Text() == "hello world!" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// and detach from and kill the headless instance
|
||||||
|
client := rpc2.NewClient(listenAddr)
|
||||||
|
if err := client.Detach(true); err != nil {
|
||||||
|
t.Fatalf("error detaching from headless instance: %v", err)
|
||||||
|
}
|
||||||
|
cmd.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
func checkAutogenDoc(t *testing.T, filename, gencommand string, generated []byte) {
|
func checkAutogenDoc(t *testing.T, filename, gencommand string, generated []byte) {
|
||||||
saved := slurpFile(t, filepath.Join(projectRoot(), filename))
|
saved := slurpFile(t, filepath.Join(projectRoot(), filename))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user