-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Changes from 47 commits
4a06698
6968a79
105696d
444eeb1
3869956
8a43523
4e47c69
8350405
9c96fbe
c4b8ce2
a605ff9
d56ac9f
984e012
63341e7
d5f5fb3
4621f4c
f433b23
ffad8a4
a1467d6
70216a5
902235d
9e08369
0b3c066
38958bc
fc89882
e6a0c78
359ee2c
ad0dec1
73c7076
61092e2
bdd13c9
f43b3d3
28cea47
c0b0d07
4617c1f
123de13
980d430
72c6718
f83ec15
31dba49
8ebdb9d
7d441c2
dfa96ba
ca76f9d
36d0e92
6e33d4a
702341d
5aeb3f8
10e0a66
1e3701b
4c8f86b
c016bc1
a4f0491
0c2f3ad
02d96ec
44fa283
768fad8
f27bfcf
7dd6f49
7725eae
38969ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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!") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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, redirects [3]proc.OutputRedirect) (run func() (string, error), stop func() error, err error) { | ||
if err := checkRRAvailable(); err != nil { | ||
return nil, nil, err | ||
} | ||
|
@@ -63,11 +63,13 @@ 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) { | ||
toclose := []*os.File{} | ||
func openRedirects(redirects [3]proc.OutputRedirect, quiet bool) (stdin, stdout, stderr *os.File, closefn func(), err error) { | ||
var ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done. |
||
toclose = []*os.File{} | ||
) | ||
|
||
if redirects[0] != "" { | ||
stdin, err = os.Open(redirects[0]) | ||
if redirects[0].Path != "" { | ||
stdin, err = os.Open(redirects[0].Path) | ||
if err != nil { | ||
return nil, nil, nil, nil, err | ||
} | ||
|
@@ -76,19 +78,25 @@ 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) | ||
|
@@ -112,7 +120,7 @@ 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) { | ||
func Record(cmd []string, wd string, quiet bool, redirects [3]proc.OutputRedirect) (tracedir string, err error) { | ||
run, _, err := RecordAsync(cmd, wd, quiet, redirects) | ||
if err != nil { | ||
return "", err | ||
|
@@ -288,7 +296,7 @@ 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) { | ||
func RecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string, redirects [3]proc.OutputRedirect) (*proc.TargetGroup, string, error) { | ||
tracedir, err := Record(cmd, wd, quiet, redirects) | ||
if tracedir == "" { | ||
return nil, "", err | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
//go:build !windows | ||
derekparker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// +build !windows | ||
|
||
package proc | ||
|
||
import ( | ||
"crypto/rand" | ||
"encoding/hex" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"sync/atomic" | ||
"syscall" | ||
) | ||
|
||
type openOnRead struct { | ||
p string | ||
rd io.ReadCloser | ||
status int32 // 0: initialization 1: file is opened 2: file is closed | ||
} | ||
|
||
func (oor *openOnRead) Read(p []byte) (n int, err error) { | ||
if oor.rd != nil { | ||
return oor.rd.Read(p) | ||
} | ||
|
||
// try from "into" to "open" | ||
if !atomic.CompareAndSwapInt32(&oor.status, 0, 1) { | ||
return 0, io.EOF | ||
} | ||
|
||
fh, err := os.OpenFile(oor.p, 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.p) | ||
|
||
// try from "init" to "close". | ||
if !atomic.CompareAndSwapInt32(&oor.status, 0, 2) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is awful, there has to be a better way. At minimum it should use a mutex instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should be enough to start the readers only after the target process has started. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can not work because rr.go (at minimum) would deadlock. However all we need to know is that the fifo has been opened for writing at least once, which we can do by unconditionally opening the fifo with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I meant this:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done. |
||
// try from "open" to "close" | ||
if atomic.CompareAndSwapInt32(&oor.status, 1, 2) { | ||
// Make the "oor.Read()" function stop blocking | ||
_, err := os.OpenFile(oor.p, os.O_WRONLY, os.ModeNamedPipe) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
return oor.rd.Close() | ||
} | ||
|
||
func NamedPipe() (reader [2]io.ReadCloser, output [3]OutputRedirect, err error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done. |
||
r := make([]byte, 4) | ||
if _, err = rand.Read(r); err != nil { | ||
return reader, output, err | ||
} | ||
|
||
var ( | ||
prefix = filepath.Join(os.TempDir(), hex.EncodeToString(r)) | ||
stdoutPath = prefix + "stdout" | ||
stderrPath = prefix + "stderr" | ||
) | ||
|
||
if err = syscall.Mkfifo(stdoutPath, 0o600); err != nil { | ||
return reader, output, err | ||
} | ||
|
||
if err := syscall.Mkfifo(stderrPath, 0o600); err != nil { | ||
_ = os.Remove(stdoutPath) | ||
return reader, output, err | ||
} | ||
|
||
reader[0] = &openOnRead{p: stdoutPath} | ||
reader[1] = &openOnRead{p: stderrPath} | ||
|
||
return reader, NewRedirectByPath([3]string{"", stdoutPath, stderrPath}), nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
//go:build windows | ||
// +build windows | ||
|
||
package proc | ||
|
||
import ( | ||
"io" | ||
"os" | ||
) | ||
|
||
func NamedPipe() (reader [2]io.ReadCloser, output [3]OutputRedirect, err error) { | ||
reader[0], output[1].File, err = os.Pipe() | ||
if err != nil { | ||
return reader, output, err | ||
} | ||
|
||
reader[1], output[2].File, err = os.Pipe() | ||
if err != nil { | ||
return reader, output, err | ||
} | ||
|
||
return reader, output, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's weird that the stdin redirect is of type OutputRedirect, and that it can never be anything but a path. I'd say this should be changed to
stdinRedirect string, stdoutRedirect, stderrRedirect proc.OutputRedirect
. The stdin redirect isn't treated like the other two at all, at this point.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the struct name should be changed back to
Redirect
.I want to keepstdin
passed in as*os.File
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? Nothing is using the *os.File version of stdin. Looking at the DAP specification I don't see anything that could use it, but maybe I'm missing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went through the
Dap
specification and didn't find definition of remote input event. Maybe there will be a specification to support remote input events.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think that this and other should be replaced by
stdin string, stdout, stderr proc.OutputRedirect
. They are not three of the same thing anymore, stdin is treated differently and there is no path for it becoming similar to the other two. Also stdin is definitely not an output.