diff --git a/Documentation/faq.md b/Documentation/faq.md index e87c1c88..9b8874eb 100644 --- a/Documentation/faq.md +++ b/Documentation/faq.md @@ -28,14 +28,10 @@ And then connect to it from outside the container: 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: - -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: +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: ``` -#!/bin/bash -while true; do sleep 1; dlv connect :4040 --init <(echo quit -c) && exit; done +dlv exec --headless --continue --listen :4040 --accept-multiclient /path/to/executable ``` Note that the connection to Delve is unauthenticated and will allow arbitrary remote code execution: *do not do this in production*. diff --git a/Documentation/usage/dlv_debug.md b/Documentation/usage/dlv_debug.md index 1aad36d6..75077c09 100644 --- a/Documentation/usage/dlv_debug.md +++ b/Documentation/usage/dlv_debug.md @@ -19,6 +19,7 @@ dlv debug [package] ### Options ``` + --continue Continue the debugged process on start. --output string Output path for the binary. (default "./__debug_bin") ``` diff --git a/Documentation/usage/dlv_exec.md b/Documentation/usage/dlv_exec.md index aadd6554..7aa843ff 100644 --- a/Documentation/usage/dlv_exec.md +++ b/Documentation/usage/dlv_exec.md @@ -17,6 +17,12 @@ or later, -gcflags="-N -l" on earlier versions of Go. dlv exec ``` +### Options + +``` + --continue Continue the debugged process on start. +``` + ### Options inherited from parent commands ``` diff --git a/cmd/dlv/cmds/commands.go b/cmd/dlv/cmds/commands.go index 729266db..1f7f6f56 100644 --- a/cmd/dlv/cmds/commands.go +++ b/cmd/dlv/cmds/commands.go @@ -33,6 +33,8 @@ var ( 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 @@ -172,6 +174,7 @@ session.`, Run: debugCmd, } 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) // 'exec' subcommand. @@ -195,6 +198,7 @@ or later, -gcflags="-N -l" on earlier versions of Go.`, os.Exit(execute(0, args, conf, "", executingExistingFile)) }, } + execCommand.Flags().BoolVar(&ContinueOnStart, "continue", false, "Continue the debugged process on start.") RootCommand.AddCommand(execCommand) // Deprecated 'run' subcommand. @@ -561,7 +565,17 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile defer logflags.Close() 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 { @@ -633,6 +647,11 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile var status int 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) signal.Notify(ch, syscall.SIGINT) select { diff --git a/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go index 8767c16c..a2f37bed 100644 --- a/cmd/dlv/dlv_test.go +++ b/cmd/dlv/dlv_test.go @@ -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) { saved := slurpFile(t, filepath.Join(projectRoot(), filename))