提交 4064d6ac 编写于 作者: E Evgeny L 提交者: Derek Parker

Flag to set working directory (#650)

* proc: Add `wd` to Launch

This change adds the `wd` arg which specify working directory of the
program.

Fixes #295

* service/debugger: Add `Wd` field to debugger.Config

This change adds the `Wd` field which specify working directory of the
program launched by debugger.

Fixes #295

* service: Add `Wd` to service.Config

This change adds the `Wd` field which specify working directory of the
program debugger will launch.

Fixes #295

* cmd/dlv: Add `Wd` flag

This change adds `Wd` flag which specify working directory of the
program which launched by debugger.

Fixes #295

* only set the Linux working directory if it is set,
stub out param in darwin and windows

* set working directory for Windows
https://godoc.org/golang.org/x/sys/windows#CreateProcess
https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx

* Windows workingDir must be an *uint16

* attempt to chdir on darwin via @yuntan

* proc/exec_darwin.c: fix working directory for darwin

* Add tests to check if working directory works.
* Fix darwin implementation of fork/exec, which paniced if
  child fork returned.

* cmd, service: rename Wd to WorkingDir
上级 f62bf8d1
package main
import (
"fmt"
"os"
)
func main() {
pwd, err := os.Getwd()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println(pwd)
}
......@@ -39,6 +39,8 @@ var (
InitFile string
// BuildFlags is the flags passed during compiler invocation.
BuildFlags string
// WorkingDir is the working directory for running the program.
WorkingDir string
// RootCommand is the root of the command tree.
RootCommand *cobra.Command
......@@ -86,6 +88,7 @@ func New() *cobra.Command {
RootCommand.PersistentFlags().IntVar(&APIVersion, "api-version", 1, "Selects API version when headless.")
RootCommand.PersistentFlags().StringVar(&InitFile, "init", "", "Init file, executed by the terminal client.")
RootCommand.PersistentFlags().StringVar(&BuildFlags, "build-flags", buildFlagsDefault, "Build flags, to be passed to the compiler.")
RootCommand.PersistentFlags().StringVar(&WorkingDir, "wd", ".", "Working directory for running the program.")
// 'attach' subcommand.
attachCommand := &cobra.Command{
......@@ -231,8 +234,12 @@ func debugCmd(cmd *cobra.Command, args []string) {
return 1
}
defer os.Remove(fp)
processArgs := append([]string{"./" + debugname}, targetArgs...)
abs, err := filepath.Abs(debugname)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return 1
}
processArgs := append([]string{abs}, targetArgs...)
return execute(0, processArgs, conf, executingGeneratedFile)
}()
os.Exit(status)
......@@ -275,6 +282,7 @@ func traceCmd(cmd *cobra.Command, args []string) {
ProcessArgs: processArgs,
AttachPid: traceAttachPid,
APIVersion: 2,
WorkingDir: WorkingDir,
}, Log)
if err := server.Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
......@@ -398,6 +406,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config, kind exec
AttachPid: attachPid,
AcceptMulti: AcceptMulti,
APIVersion: APIVersion,
WorkingDir: WorkingDir,
}, Log)
default:
fmt.Println("Unknown API version %d", APIVersion)
......
#include "exec_darwin.h"
#include "stdio.h"
extern char** environ;
......@@ -12,6 +13,7 @@ close_exec_pipe(int fd[2]) {
int
fork_exec(char *argv0, char **argv, int size,
char *wd,
task_t *task,
mach_port_t *port_set,
mach_port_t *exception_port,
......@@ -53,7 +55,7 @@ fork_exec(char *argv0, char **argv, int size,
}
// Fork succeeded, we are in the child.
int pret;
int pret, cret;
char sig;
close(fd[1]);
......@@ -62,7 +64,8 @@ fork_exec(char *argv0, char **argv, int size,
// Create a new process group.
if (setpgid(0, 0) < 0) {
return -1;
perror("setpgid");
exit(1);
}
// Set errno to zero before a call to ptrace.
......@@ -70,11 +73,29 @@ fork_exec(char *argv0, char **argv, int size,
// for successful calls.
errno = 0;
pret = ptrace(PT_TRACE_ME, 0, 0, 0);
if (pret != 0 && errno != 0) return -errno;
if (pret != 0 && errno != 0) {
perror("ptrace");
exit(1);
}
// Change working directory if wd is not empty.
if (wd && wd[0]) {
errno = 0;
cret = chdir(wd);
if (cret != 0 && errno != 0) {
char *error_msg;
asprintf(&error_msg, "%s '%s'", "chdir", wd);
perror(error_msg);
exit(1);
}
}
errno = 0;
pret = ptrace(PT_SIGEXC, 0, 0, 0);
if (pret != 0 && errno != 0) return -errno;
if (pret != 0 && errno != 0) {
perror("ptrace");
exit(1);
}
// Create the child process.
execve(argv0, argv, environ);
......
......@@ -7,4 +7,4 @@
#include <fcntl.h>
int
fork_exec(char *, char **, int, task_t*, mach_port_t*, mach_port_t*, mach_port_t*);
fork_exec(char *, char **, int, char *, task_t*, mach_port_t*, mach_port_t*, mach_port_t*);
......@@ -37,12 +37,11 @@ type OSProcessDetails struct {
// 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) (*Process, error) {
func Launch(cmd []string, wd string) (*Process, error) {
// check that the argument to Launch is an executable file
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
return nil, NotExecutableErr
}
argv0Go, err := filepath.Abs(cmd[0])
if err != nil {
return nil, err
......@@ -69,6 +68,7 @@ func Launch(cmd []string) (*Process, error) {
var pid int
dbp.execPtraceFunc(func() {
ret := C.fork_exec(argv0, &argvSlice[0], C.int(len(argvSlice)),
C.CString(wd),
&dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort,
&dbp.os.notificationPort)
pid = int(ret)
......
......@@ -45,8 +45,8 @@ type OSProcessDetails struct {
// Launch creates and begins debugging a new process. First entry in
// `cmd` is the program to run, and then rest are the arguments
// to be supplied to that process.
func Launch(cmd []string) (*Process, error) {
// to be supplied to that process. `wd` is working directory of the program.
func Launch(cmd []string, wd string) (*Process, error) {
var (
proc *exec.Cmd
err error
......@@ -62,6 +62,9 @@ func Launch(cmd []string) (*Process, error) {
proc.Stdout = os.Stdout
proc.Stderr = os.Stderr
proc.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true}
if wd != "" {
proc.Dir = wd
}
err = proc.Start()
})
if err != nil {
......
......@@ -33,7 +33,7 @@ func TestMain(m *testing.M) {
func withTestProcess(name string, t testing.TB, fn func(p *Process, fixture protest.Fixture)) {
fixture := protest.BuildFixture(name)
p, err := Launch([]string{fixture.Path})
p, err := Launch([]string{fixture.Path}, ".")
if err != nil {
t.Fatal("Launch():", err)
}
......@@ -46,9 +46,9 @@ func withTestProcess(name string, t testing.TB, fn func(p *Process, fixture prot
fn(p, fixture)
}
func withTestProcessArgs(name string, t testing.TB, fn func(p *Process, fixture protest.Fixture), args []string) {
func withTestProcessArgs(name string, t testing.TB, wd string, fn func(p *Process, fixture protest.Fixture), args []string) {
fixture := protest.BuildFixture(name)
p, err := Launch(append([]string{fixture.Path}, args...))
p, err := Launch(append([]string{fixture.Path}, args...), wd)
if err != nil {
t.Fatal("Launch():", err)
}
......@@ -1739,17 +1739,17 @@ func TestCmdLineArgs(t *testing.T) {
}
// make sure multiple arguments (including one with spaces) are passed to the binary correctly
withTestProcessArgs("testargs", t, expectSuccess, []string{"test"})
withTestProcessArgs("testargs", t, expectSuccess, []string{"test", "pass flag"})
withTestProcessArgs("testargs", t, ".", expectSuccess, []string{"test"})
withTestProcessArgs("testargs", t, ".", expectSuccess, []string{"test", "pass flag"})
// check that arguments with spaces are *only* passed correctly when correctly called
withTestProcessArgs("testargs", t, expectPanic, []string{"test pass", "flag"})
withTestProcessArgs("testargs", t, expectPanic, []string{"test", "pass", "flag"})
withTestProcessArgs("testargs", t, expectPanic, []string{"test pass flag"})
withTestProcessArgs("testargs", t, ".", expectPanic, []string{"test pass", "flag"})
withTestProcessArgs("testargs", t, ".", expectPanic, []string{"test", "pass", "flag"})
withTestProcessArgs("testargs", t, ".", expectPanic, []string{"test pass flag"})
// and that invalid cases (wrong arguments or no arguments) panic
withTestProcess("testargs", t, expectPanic)
withTestProcessArgs("testargs", t, expectPanic, []string{"invalid"})
withTestProcessArgs("testargs", t, expectPanic, []string{"test", "invalid"})
withTestProcessArgs("testargs", t, expectPanic, []string{"invalid", "pass flag"})
withTestProcessArgs("testargs", t, ".", expectPanic, []string{"invalid"})
withTestProcessArgs("testargs", t, ".", expectPanic, []string{"test", "invalid"})
withTestProcessArgs("testargs", t, ".", expectPanic, []string{"invalid", "pass flag"})
}
func TestIssue462(t *testing.T) {
......@@ -1872,7 +1872,7 @@ func TestIssue509(t *testing.T) {
cmd.Dir = nomaindir
assertNoError(cmd.Run(), t, "go build")
exepath := filepath.Join(nomaindir, "debug")
_, err := Launch([]string{exepath})
_, err := Launch([]string{exepath}, ".")
if err == nil {
t.Fatalf("expected error but none was generated")
}
......@@ -1906,7 +1906,7 @@ func TestUnsupportedArch(t *testing.T) {
}
defer os.Remove(outfile)
p, err := Launch([]string{outfile})
p, err := Launch([]string{outfile}, ".")
switch err {
case UnsupportedArchErr:
// all good
......@@ -2306,3 +2306,23 @@ func TestStepOutPanicAndDirectCall(t *testing.T) {
}
})
}
func TestWorkDir(t *testing.T) {
wd := os.TempDir()
// For Darwin `os.TempDir()` returns `/tmp` which is symlink to `/private/tmp`.
if runtime.GOOS == "darwin" {
wd = "/private/tmp"
}
withTestProcessArgs("workdir", t, wd, func(p *Process, fixture protest.Fixture) {
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 14)
assertNoError(err, t, "LineToPC")
p.SetBreakpoint(addr, UserBreakpoint, nil)
p.Continue()
v, err := evalVariable(p, "pwd")
assertNoError(err, t, "EvalVariable")
str := constant.StringVal(v.Value)
if wd != str {
t.Fatalf("Expected %s got %s\n", wd, str)
}
}, []string{})
}
......@@ -26,7 +26,7 @@ type OSProcessDetails struct {
}
// Launch creates and begins debugging a new process.
func Launch(cmd []string) (*Process, error) {
func Launch(cmd []string, wd string) (*Process, error) {
argv0Go, err := filepath.Abs(cmd[0])
if err != nil {
return nil, err
......@@ -82,6 +82,13 @@ func Launch(cmd []string) (*Process, error) {
return nil, err
}
}
var workingDir *uint16
if wd != "" {
if workingDir, err = syscall.UTF16PtrFromString(wd); err != nil {
return nil, err
}
}
// Initialize the startup info and create process
si := new(sys.StartupInfo)
......@@ -94,7 +101,11 @@ func Launch(cmd []string) (*Process, error) {
dbp := New(0)
dbp.execPtraceFunc(func() {
err = sys.CreateProcess(argv0, cmdLine, nil, nil, true, _DEBUG_ONLY_THIS_PROCESS, nil, nil, si, pi)
if wd == "" {
err = sys.CreateProcess(argv0, cmdLine, nil, nil, true, _DEBUG_ONLY_THIS_PROCESS, nil, nil, si, pi)
} else {
err = sys.CreateProcess(argv0, cmdLine, nil, nil, true, _DEBUG_ONLY_THIS_PROCESS, nil, workingDir, si, pi)
}
})
if err != nil {
return nil, err
......
......@@ -13,6 +13,10 @@ type Config struct {
Listener net.Listener
// ProcessArgs are the arguments to launch a new process.
ProcessArgs []string
// WorkingDir is working directory of the new process. This field is used
// only when launching a new process.
WorkingDir string
// AttachPid is the PID of an existing process to which the debugger should
// attach.
AttachPid int
......
......@@ -38,6 +38,10 @@ type Debugger struct {
type Config struct {
// ProcessArgs are the arguments to launch a new process.
ProcessArgs []string
// WorkingDir is working directory of the new process. This field is used
// only when launching a new process.
WorkingDir string
// AttachPid is the PID of an existing process to which the debugger should
// attach.
AttachPid int
......@@ -59,7 +63,7 @@ func New(config *Config) (*Debugger, error) {
d.process = p
} else {
log.Printf("launching process with args: %v", d.config.ProcessArgs)
p, err := proc.Launch(d.config.ProcessArgs)
p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir)
if err != nil {
if err != proc.NotExecutableErr && err != proc.UnsupportedArchErr {
err = fmt.Errorf("could not launch process: %s", err)
......@@ -112,7 +116,7 @@ func (d *Debugger) Restart() error {
return err
}
}
p, err := proc.Launch(d.config.ProcessArgs)
p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir)
if err != nil {
return fmt.Errorf("could not launch process: %s", err)
}
......
......@@ -111,6 +111,7 @@ func (s *ServerImpl) Run() error {
if s.debugger, err = debugger.New(&debugger.Config{
ProcessArgs: s.config.ProcessArgs,
AttachPid: s.config.AttachPid,
WorkingDir: s.config.WorkingDir,
}); err != nil {
return err
}
......
......@@ -79,7 +79,7 @@ const varTestBreakpointLineNumber = 59
func withTestProcess(name string, t *testing.T, fn func(p *proc.Process, fixture protest.Fixture)) {
fixture := protest.BuildFixture(name)
p, err := proc.Launch([]string{fixture.Path})
p, err := proc.Launch([]string{fixture.Path}, ".")
if err != nil {
t.Fatal("Launch():", err)
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册