Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supports sending output to clients when running programs remotely #3253

Merged
merged 61 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
4a06698
wip: Support sending output when remote debug
tttoad Jan 17, 2023
6968a79
wip: Support local output and remote output
tttoad Jan 19, 2023
105696d
wip: fix stderr and stdout assignment error
tttoad Jan 19, 2023
444eeb1
wip: optimize code
tttoad Jan 20, 2023
3869956
wip: Only if outputMode is "remote" is the redirected console output
tttoad Jan 21, 2023
8a43523
wip: Redirected debugMode output(Not tested on windows)
tttoad Jan 31, 2023
4e47c69
wip: support remote debugging output redirection of windows
tttoad Jan 31, 2023
8350405
wip: real-time write back output
tttoad Feb 1, 2023
9c96fbe
wip: support for windows
tttoad Feb 2, 2023
c4b8ce2
wip: fix windows remote debug not output
tttoad Feb 2, 2023
a605ff9
wip: fix truncated output redirection
tttoad Feb 2, 2023
d56ac9f
Merge branch 'master' of github.com:tttoad/delve into feat-console
tttoad Feb 2, 2023
984e012
wip: delete printfln
tttoad Feb 3, 2023
63341e7
wip: use debugger.Config to pass redirect(macOS)
tttoad Feb 9, 2023
d5f5fb3
wip: use debugger.Config to pass redirect(linux)
tttoad Feb 11, 2023
4621f4c
wip: Change redirect to a concrete type
tttoad Feb 11, 2023
f433b23
wip: s.wg.wait before sending "terminated"
tttoad Feb 11, 2023
ffad8a4
wip: add proc/redirect test(darwin and linux)
tttoad Feb 17, 2023
a1467d6
Merge branch 'master' of github.com:tttoad/delve into feat-console
tttoad Feb 18, 2023
70216a5
Merge branch 'master' of github.com:tttoad/delve into feat-console
tttoad Feb 19, 2023
902235d
wip: Fix test failure on windows
tttoad Feb 20, 2023
9e08369
fix: undefined: proc.Redirects
tttoad Feb 20, 2023
0b3c066
fix: compile failure
tttoad Feb 20, 2023
38958bc
wip: Remove useless code
tttoad Feb 20, 2023
fc89882
fix: filename error
tttoad Feb 23, 2023
e6a0c78
Merge branch 'master' of github.com:tttoad/delve into feat-console
tttoad Feb 26, 2023
359ee2c
fix: os.file not close
tttoad Feb 26, 2023
ad0dec1
test: add server_test.redirect
tttoad Mar 1, 2023
73c7076
fix: Remove 'eol' from end of file
tttoad Mar 2, 2023
61092e2
fix: gdbserial: File not closed in file mode.
tttoad Mar 2, 2023
bdd13c9
feat: Remove "only-remote". Fix spelling mistakes.
tttoad Mar 3, 2023
f43b3d3
fix: spelling mistakes
tttoad Mar 3, 2023
28cea47
refactor: redirect
tttoad Mar 8, 2023
c0b0d07
fix: stdout and stderr are not set to default values
tttoad Mar 9, 2023
4617c1f
fix: Restore code logic for rr.openRedirects()
tttoad Mar 9, 2023
123de13
fix: Optimization Code
tttoad Mar 15, 2023
980d430
fix: utiltest
tttoad Mar 15, 2023
72c6718
fix: execpt out
tttoad Mar 15, 2023
f83ec15
fix: Resource release for redirects
tttoad Mar 18, 2023
31dba49
fix: build failure
tttoad Mar 18, 2023
8ebdb9d
fix: clean->clear
tttoad Mar 18, 2023
7d441c2
fix: build failure
tttoad Mar 19, 2023
dfa96ba
Merge branch 'master' of github.com:tttoad/delve into feat-console
tttoad Mar 19, 2023
ca76f9d
fix: test failure
tttoad Mar 19, 2023
36d0e92
fix: Optimization Code
Mar 20, 2023
6e33d4a
style: remove useless code
tttoad Mar 27, 2023
702341d
refactor: namedpipe
tttoad Apr 1, 2023
5aeb3f8
refactor: namedpipe, launch ...
tttoad Apr 4, 2023
10e0a66
fix: freebsd compile failure
tttoad Apr 4, 2023
1e3701b
fix: proc_darwin compile failure
tttoad Apr 4, 2023
4c8f86b
style: remove useless code
tttoad Apr 4, 2023
c016bc1
Merge branch 'master' of github.com:tttoad/delve into feat-console
tttoad Apr 5, 2023
a4f0491
feat: add d.config.Stdxx check on debug.Restart
tttoad Apr 8, 2023
0c2f3ad
style: formatting and adding comments
tttoad May 1, 2023
02d96ec
style: formatting and adding comments
tttoad May 1, 2023
44fa283
feat: add d.config.Stdxx check on debug.Restart
tttoad Apr 8, 2023
768fad8
style: namedpipe->redirector
tttoad May 12, 2023
f27bfcf
Merge branch 'feat-console' of github.com:tttoad/delve into feat-console
tttoad May 12, 2023
7dd6f49
style: namedPipe->redirector
tttoad May 12, 2023
7725eae
Merge branch 'master' into feat-console
tttoad Jun 13, 2023
38969ab
Merge branch 'master' of github.com:tttoad/delve into feat-console
tttoad Jun 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions _fixtures/out_redirect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package main

import (
"fmt"
"os"
)

func main() {
fmt.Println("hello world!")
fmt.Fprintf(os.Stdout, "hello world!")
fmt.Fprintf(os.Stderr, "hello world!\n")
fmt.Fprintf(os.Stderr, "hello world! error!")
}
5 changes: 4 additions & 1 deletion cmd/dlv/cmds/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/go-delve/delve/pkg/gobuild"
"github.com/go-delve/delve/pkg/goversion"
"github.com/go-delve/delve/pkg/logflags"
"github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/terminal"
"github.com/go-delve/delve/pkg/version"
"github.com/go-delve/delve/service"
Expand Down Expand Up @@ -1015,7 +1016,9 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
DebugInfoDirectories: conf.DebugInfoDirectories,
CheckGoVersion: checkGoVersion,
TTY: tty,
Redirects: redirects,
Stdin: redirects[0],
Stdout: proc.OutputRedirect{Path: redirects[1]},
Stderr: proc.OutputRedirect{Path: redirects[2]},
DisableASLR: disableASLR,
RrOnProcessPid: rrOnProcessPid,
},
Expand Down
48 changes: 27 additions & 21 deletions pkg/proc/gdbserial/rr.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
// program. Returns a run function which will actually record the program, a
// stop function which will prematurely terminate the recording of the
// program.
func RecordAsync(cmd []string, wd string, quiet bool, redirects [3]string) (run func() (string, error), stop func() error, err error) {
func RecordAsync(cmd []string, wd string, quiet bool, stdin string, stdout proc.OutputRedirect, stderr proc.OutputRedirect) (run func() (string, error), stop func() error, err error) {
if err := checkRRAvailable(); err != nil {
return nil, nil, err
}
Expand All @@ -35,7 +35,7 @@ func RecordAsync(cmd []string, wd string, quiet bool, redirects [3]string) (run
args = append(args, cmd...)
rrcmd := exec.Command("rr", args...)
var closefn func()
rrcmd.Stdin, rrcmd.Stdout, rrcmd.Stderr, closefn, err = openRedirects(redirects, quiet)
rrcmd.Stdin, rrcmd.Stdout, rrcmd.Stderr, closefn, err = openRedirects(stdin, stdout, stderr, quiet)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -63,11 +63,11 @@ func RecordAsync(cmd []string, wd string, quiet bool, redirects [3]string) (run
return run, stop, nil
}

func openRedirects(redirects [3]string, quiet bool) (stdin, stdout, stderr *os.File, closefn func(), err error) {
func openRedirects(stdinPath string, stdoutOR proc.OutputRedirect, stderrOR proc.OutputRedirect, quiet bool) (stdin, stdout, stderr *os.File, closefn func(), err error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just use stdin, stdout, stderr

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stdin, stdout, stderr is in conflict with the returned parameter name.

toclose := []*os.File{}

if redirects[0] != "" {
stdin, err = os.Open(redirects[0])
if stdinPath != "" {
stdin, err = os.Open(stdinPath)
if err != nil {
return nil, nil, nil, nil, err
}
Expand All @@ -76,27 +76,33 @@ func openRedirects(redirects [3]string, quiet bool) (stdin, stdout, stderr *os.F
stdin = os.Stdin
}

create := func(path string, dflt *os.File) *os.File {
if path == "" {
if quiet {
return nil
create := func(redirect proc.OutputRedirect, dflt *os.File) (f *os.File) {
if redirect.Path != "" {
f, err = os.Create(redirect.Path)
if f != nil {
toclose = append(toclose, f)
}
return dflt

return f
} else if redirect.File != nil {
toclose = append(toclose, redirect.File)

return redirect.File
}
var f *os.File
f, err = os.Create(path)
if f != nil {
toclose = append(toclose, f)

if quiet {
return nil
}
return f

return dflt
}

stdout = create(redirects[1], os.Stdout)
stdout = create(stdoutOR, os.Stdout)
if err != nil {
return nil, nil, nil, nil, err
}

stderr = create(redirects[2], os.Stderr)
stderr = create(stderrOR, os.Stderr)
if err != nil {
return nil, nil, nil, nil, err
}
Expand All @@ -112,8 +118,8 @@ func openRedirects(redirects [3]string, quiet bool) (stdin, stdout, stderr *os.F

// Record uses rr to record the execution of the specified program and
// returns the trace directory's path.
func Record(cmd []string, wd string, quiet bool, redirects [3]string) (tracedir string, err error) {
run, _, err := RecordAsync(cmd, wd, quiet, redirects)
func Record(cmd []string, wd string, quiet bool, stdin string, stdout proc.OutputRedirect, stderr proc.OutputRedirect) (tracedir string, err error) {
run, _, err := RecordAsync(cmd, wd, quiet, stdin, stdout, stderr)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -288,8 +294,8 @@ func rrParseGdbCommand(line string) rrInit {
}

// RecordAndReplay acts like calling Record and then Replay.
func RecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string, redirects [3]string) (*proc.TargetGroup, string, error) {
tracedir, err := Record(cmd, wd, quiet, redirects)
func RecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string, stdin string, stdout proc.OutputRedirect, stderr proc.OutputRedirect) (*proc.TargetGroup, string, error) {
tracedir, err := Record(cmd, wd, quiet, stdin, stdout, stderr)
if tracedir == "" {
return nil, "", err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/proc/gdbserial/rr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func withTestRecording(name string, t testing.TB, fn func(grp *proc.TargetGroup,
t.Skip("test skipped, rr not found")
}
t.Log("recording")
grp, tracedir, err := gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true, []string{}, [3]string{})
grp, tracedir, err := gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true, []string{}, "", proc.OutputRedirect{}, proc.OutputRedirect{})
if err != nil {
t.Fatal("Launch():", err)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/proc/native/nonative_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation")

// Launch returns ErrNativeBackendDisabled.
func Launch(_ []string, _ string, _ proc.LaunchFlags, _ []string, _ string, _ [3]string) (*proc.TargetGroup, error) {
func Launch(_ []string, _ string, _ proc.LaunchFlags, _ []string, _ string, _ string, _ proc.OutputRedirect, _ proc.OutputRedirect) (*proc.TargetGroup, error) {
return nil, ErrNativeBackendDisabled
}

Expand Down
33 changes: 19 additions & 14 deletions pkg/proc/native/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,11 +376,11 @@ func (dbp *nativeProcess) writeSoftwareBreakpoint(thread *nativeThread, addr uin
return err
}

func openRedirects(redirects [3]string, foreground bool) (stdin, stdout, stderr *os.File, closefn func(), err error) {
func openRedirects(stdinPath string, stdoutOR proc.OutputRedirect, stderrOR proc.OutputRedirect, foreground bool) (stdin, stdout, stderr *os.File, closefn func(), err error) {
toclose := []*os.File{}

if redirects[0] != "" {
stdin, err = os.Open(redirects[0])
if stdinPath != "" {
stdin, err = os.Open(stdinPath)
if err != nil {
return nil, nil, nil, nil, err
}
Expand All @@ -389,24 +389,29 @@ func openRedirects(redirects [3]string, foreground bool) (stdin, stdout, stderr
stdin = os.Stdin
}

create := func(path string, dflt *os.File) *os.File {
if path == "" {
return dflt
}
var f *os.File
f, err = os.Create(path)
if f != nil {
toclose = append(toclose, f)
create := func(redirect proc.OutputRedirect, dflt *os.File) (f *os.File) {
if redirect.Path != "" {
f, err = os.Create(redirect.Path)
if f != nil {
toclose = append(toclose, f)
}

return f
} else if redirect.File != nil {
toclose = append(toclose, redirect.File)

return redirect.File
}
return f

return dflt
}

stdout = create(redirects[1], os.Stdout)
stdout = create(stdoutOR, os.Stdout)
if err != nil {
return nil, nil, nil, nil, err
}

stderr = create(redirects[2], os.Stderr)
stderr = create(stderrOR, os.Stderr)
if err != nil {
return nil, nil, nil, nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/proc/native/proc_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (os *osProcessDetails) Close() {}
// custom fork/exec process in order to take advantage of
// PT_SIGEXC on Darwin which will turn Unix signals into
// Mach exceptions.
func Launch(cmd []string, wd string, flags proc.LaunchFlags, _ []string, _ string, _ [3]string) (*proc.TargetGroup, error) {
func Launch(cmd []string, wd string, flags proc.LaunchFlags, _ []string, _ string, _ string, _ proc.OutputRedirect, _ proc.OutputRedirect) (*proc.TargetGroup, error) {
argv0Go, err := filepath.Abs(cmd[0])
if err != nil {
return nil, err
Expand Down
4 changes: 2 additions & 2 deletions pkg/proc/native/proc_freebsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ func (os *osProcessDetails) Close() {}
// to be supplied to that process. `wd` is working directory of the program.
// If the DWARF information cannot be found in the binary, Delve will look
// for external debug files in the directories passed in.
func Launch(cmd []string, wd string, flags proc.LaunchFlags, debugInfoDirs []string, tty string, redirects [3]string) (*proc.TargetGroup, error) {
func Launch(cmd []string, wd string, flags proc.LaunchFlags, debugInfoDirs []string, tty string, stdinPath string, stdoutOR proc.OutputRedirect, stderrOR proc.OutputRedirect) (*proc.TargetGroup, error) {
var (
process *exec.Cmd
err error
)

foreground := flags&proc.LaunchForeground != 0

stdin, stdout, stderr, closefn, err := openRedirects(redirects, foreground)
stdin, stdout, stderr, closefn, err := openRedirects(stdinPath, stdoutOR, stderrOR, foreground)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/proc/native/proc_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ func (os *osProcessDetails) Close() {
// to be supplied to that process. `wd` is working directory of the program.
// If the DWARF information cannot be found in the binary, Delve will look
// for external debug files in the directories passed in.
func Launch(cmd []string, wd string, flags proc.LaunchFlags, debugInfoDirs []string, tty string, redirects [3]string) (*proc.TargetGroup, error) {
func Launch(cmd []string, wd string, flags proc.LaunchFlags, debugInfoDirs []string, tty string, stdinPath string, stdoutOR proc.OutputRedirect, stderrOR proc.OutputRedirect) (*proc.TargetGroup, error) {
var (
process *exec.Cmd
err error
)

foreground := flags&proc.LaunchForeground != 0

stdin, stdout, stderr, closefn, err := openRedirects(redirects, foreground)
stdin, stdout, stderr, closefn, err := openRedirects(stdinPath, stdoutOR, stderrOR, foreground)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/proc/native/proc_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ type osProcessDetails struct {
func (os *osProcessDetails) Close() {}

// Launch creates and begins debugging a new process.
func Launch(cmd []string, wd string, flags proc.LaunchFlags, _ []string, _ string, redirects [3]string) (*proc.TargetGroup, error) {
func Launch(cmd []string, wd string, flags proc.LaunchFlags, _ []string, _ string, stdinPath string, stdoutOR proc.OutputRedirect, stderrOR proc.OutputRedirect) (*proc.TargetGroup, error) {
argv0Go := cmd[0]

env := proc.DisableAsyncPreemptEnv()

stdin, stdout, stderr, closefn, err := openRedirects(redirects, true)
stdin, stdout, stderr, closefn, err := openRedirects(stdinPath, stdoutOR, stderrOR, true)
if err != nil {
return nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/proc/proc_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"path/filepath"
"testing"

"github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/native"
protest "github.com/go-delve/delve/pkg/proc/test"
)
Expand All @@ -14,7 +15,7 @@ func TestLoadingExternalDebugInfo(t *testing.T) {
fixture := protest.BuildFixture("locationsprog", 0)
defer os.Remove(fixture.Path)
stripAndCopyDebugInfo(fixture, t)
p, err := native.Launch(append([]string{fixture.Path}, ""), "", 0, []string{filepath.Dir(fixture.Path)}, "", [3]string{})
p, err := native.Launch(append([]string{fixture.Path}, ""), "", 0, []string{filepath.Dir(fixture.Path)}, "", "", proc.OutputRedirect{}, proc.OutputRedirect{})
if err != nil {
t.Fatal(err)
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/proc/proc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,13 @@ func withTestProcessArgs(name string, t testing.TB, wd string, args []string, bu

switch testBackend {
case "native":
grp, err = native.Launch(append([]string{fixture.Path}, args...), wd, 0, []string{}, "", [3]string{})
grp, err = native.Launch(append([]string{fixture.Path}, args...), wd, 0, []string{}, "", "", proc.OutputRedirect{}, proc.OutputRedirect{})
case "lldb":
grp, err = gdbserial.LLDBLaunch(append([]string{fixture.Path}, args...), wd, 0, []string{}, "", [3]string{})
case "rr":
protest.MustHaveRecordingAllowed(t)
t.Log("recording")
grp, tracedir, err = gdbserial.RecordAndReplay(append([]string{fixture.Path}, args...), wd, true, []string{}, [3]string{})
grp, tracedir, err = gdbserial.RecordAndReplay(append([]string{fixture.Path}, args...), wd, true, []string{}, "", proc.OutputRedirect{}, proc.OutputRedirect{})
t.Logf("replaying %q", tracedir)
default:
t.Fatal("unknown backend")
Expand Down Expand Up @@ -2247,7 +2247,7 @@ func TestUnsupportedArch(t *testing.T) {

switch testBackend {
case "native":
p, err = native.Launch([]string{outfile}, ".", 0, []string{}, "", [3]string{})
p, err = native.Launch([]string{outfile}, ".", 0, []string{}, "", "", proc.OutputRedirect{}, proc.OutputRedirect{})
case "lldb":
p, err = gdbserial.LLDBLaunch([]string{outfile}, ".", 0, []string{}, "", [3]string{})
default:
Expand Down
12 changes: 12 additions & 0 deletions pkg/proc/redirect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package proc

import "os"

// OutputRedirect Specifies where the target program output will be redirected to.
// Only one of "Path" and "File" should be set.
type OutputRedirect struct {
derekparker marked this conversation as resolved.
Show resolved Hide resolved
// Path File path.
Path string
// File Redirect file.
File *os.File
}
59 changes: 59 additions & 0 deletions pkg/proc/redirector_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//go:build !windows
// +build !windows

package proc

import (
"crypto/rand"
"encoding/hex"
"io"
"os"
"path/filepath"
"syscall"
)

type openOnRead struct {
path string
rd io.ReadCloser
}

func (oor *openOnRead) Read(p []byte) (n int, err error) {
if oor.rd != nil {
return oor.rd.Read(p)
}

fh, err := os.OpenFile(oor.path, os.O_RDONLY, os.ModeNamedPipe)
if err != nil {
return 0, err
}

oor.rd = fh
return oor.rd.Read(p)
}

func (oor *openOnRead) Close() error {
defer os.Remove(oor.path)

fh, _ := os.OpenFile(oor.path, os.O_WRONLY|syscall.O_NONBLOCK, 0)
if fh != nil {
fh.Close()
}

return oor.rd.Close()
}

func Redirector() (reader io.ReadCloser, output OutputRedirect, err error) {
r := make([]byte, 4)
if _, err = rand.Read(r); err != nil {
return reader, output, err
}

var path = filepath.Join(os.TempDir(), hex.EncodeToString(r))

if err = syscall.Mkfifo(path, 0o600); err != nil {
_ = os.Remove(path)
return reader, output, err
}

return &openOnRead{path: path}, OutputRedirect{Path: path}, nil
}
15 changes: 15 additions & 0 deletions pkg/proc/redirector_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build windows
// +build windows

package proc

import (
"io"
"os"
)

func Redirector() (reader io.ReadCloser, output OutputRedirect, err error) {
reader, output.File, err = os.Pipe()

return reader, output, err
}
Loading