Skip to content

Commit

Permalink
Fix spaces on long process names for MacOS
Browse files Browse the repository at this point in the history
  • Loading branch information
jblesener committed Feb 16, 2021
1 parent 3585d27 commit e8b2bea
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 37 deletions.
57 changes: 38 additions & 19 deletions process/process_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type _Ctype_struct___0 struct {
func pidsWithContext(ctx context.Context) ([]int32, error) {
var ret []int32

pids, err := callPsWithContext(ctx, "pid", 0, false)
pids, err := callPsWithContext(ctx, "pid", 0, false, false)
if err != nil {
return ret, err
}
Expand All @@ -55,7 +55,7 @@ func pidsWithContext(ctx context.Context) ([]int32, error) {
}

func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
r, err := callPsWithContext(ctx, "ppid", p.Pid, false)
r, err := callPsWithContext(ctx, "ppid", p.Pid, false, false)
if err != nil {
return 0, err
}
Expand All @@ -76,16 +76,16 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
name := common.IntToString(k.Proc.P_comm[:])

if len(name) >= 15 {
cmdlineSlice, err := p.CmdlineSliceWithContext(ctx)
cmdName, err := p.CmdNameWithContext(ctx)
if err != nil {
return "", err
}
if len(cmdlineSlice) > 0 {
extendedName := filepath.Base(cmdlineSlice[0])
if len(cmdName) > 0 {
extendedName := filepath.Base(cmdName[0])
if strings.HasPrefix(extendedName, p.name) {
name = extendedName
} else {
name = cmdlineSlice[0]
name = cmdName[0]
}
}
}
Expand All @@ -94,28 +94,37 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
}

func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
r, err := callPsWithContext(ctx, "command", p.Pid, false)
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
if err != nil {
return "", err
}
return strings.Join(r[0], " "), err
}

// CmdNameWithContext returns the command name (including spaces) without any arguments
func (p *Process) CmdNameWithContext(ctx context.Context) ([]string, error) {
r, err := callPsWithContext(ctx, "command", p.Pid, false, true)
if err != nil {
return nil, err
}
return r[0], err
}

// CmdlineSliceWithContext returns the command line arguments of the process as a slice with each
// element being an argument. Because of current deficiencies in the way that the command
// line arguments are found, single arguments that have spaces in the will actually be
// reported as two separate items. In order to do something better CGO would be needed
// to use the native darwin functions.
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
r, err := callPsWithContext(ctx, "command", p.Pid, false)
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
if err != nil {
return nil, err
}
return r[0], err
}

func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
r, err := callPsWithContext(ctx, "etime", p.Pid, false)
r, err := callPsWithContext(ctx, "etime", p.Pid, false, false)
if err != nil {
return 0, err
}
Expand Down Expand Up @@ -163,7 +172,7 @@ func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
}

func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
r, err := callPsWithContext(ctx, "state", p.Pid, false)
r, err := callPsWithContext(ctx, "state", p.Pid, false, false)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -255,7 +264,7 @@ func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, e
}

func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true)
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true, false)
if err != nil {
return 0, err
}
Expand Down Expand Up @@ -309,7 +318,7 @@ func convertCPUTimes(s string) (ret float64, err error) {
}

func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false)
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false, false)

if err != nil {
return nil, err
Expand All @@ -333,7 +342,7 @@ func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error)
}

func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false)
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false, false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -419,9 +428,9 @@ func (p *Process) getKProc() (*KinfoProc, error) {

// call ps command.
// Return value deletes Header line(you must not input wrong arg).
// And splited by Space. Caller have responsibility to manage.
// And split by space. Caller have responsibility to manage.
// If passed arg pid is 0, get information from all process.
func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption bool) ([][]string, error) {
func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption bool, nameOption bool) ([][]string, error) {
bin, err := exec.LookPath("ps")
if err != nil {
return [][]string{}, err
Expand All @@ -435,6 +444,10 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption
} else {
cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))}
}

if nameOption {
cmd = append(cmd, "-c")
}
out, err := invoke.CommandWithContext(ctx, bin, cmd...)
if err != nil {
return [][]string{}, err
Expand All @@ -443,13 +456,19 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption

var ret [][]string
for _, l := range lines[1:] {

var lr []string
for _, r := range strings.Split(l, " ") {
if r == "" {
continue
if nameOption {
lr = append(lr, l)
} else {
for _, r := range strings.Split(l, " ") {
if r == "" {
continue
}
lr = append(lr, strings.TrimSpace(r))
}
lr = append(lr, strings.TrimSpace(r))
}

if len(lr) != 0 {
ret = append(ret, lr)
}
Expand Down
46 changes: 46 additions & 0 deletions process/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,52 @@ func Test_Process_Name(t *testing.T) {
t.Errorf("invalid Exe %s", n)
}
}

func Test_Process_Long_Name_With_Spaces(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("unable to create temp dir %v", err)
}
defer os.RemoveAll(tmpdir) // clean up
tmpfilepath := filepath.Join(tmpdir, "loooong name with spaces.go")
tmpfile, err := os.Create(tmpfilepath)
if err != nil {
t.Fatalf("unable to create temp file %v", err)
}

tmpfilecontent := []byte("package main\nimport(\n\"time\"\n)\nfunc main(){\nfor range time.Tick(time.Second) {}\n}")
if _, err := tmpfile.Write(tmpfilecontent); err != nil {
tmpfile.Close()
t.Fatalf("unable to write temp file %v", err)
}
if err := tmpfile.Close(); err != nil {
t.Fatalf("unable to close temp file %v", err)
}

err = exec.Command("go", "build", "-o", tmpfile.Name()+".exe", tmpfile.Name()).Run()
if err != nil {
t.Fatalf("unable to build temp file %v", err)
}

cmd := exec.Command(tmpfile.Name() + ".exe")

assert.Nil(t, cmd.Start())
time.Sleep(100 * time.Millisecond)
p, err := NewProcess(int32(cmd.Process.Pid))
skipIfNotImplementedErr(t, err)
assert.Nil(t, err)

n, err := p.Name()
skipIfNotImplementedErr(t, err)
if err != nil {
t.Fatalf("getting name error %v", err)
}
basename := filepath.Base(tmpfile.Name() + ".exe")
if basename != n {
t.Fatalf("%s != %s", basename, n)
}
cmd.Process.Kill()
}
func Test_Process_Long_Name(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
Expand Down
52 changes: 34 additions & 18 deletions v3/process/process_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type _Ctype_struct___0 struct {
func pidsWithContext(ctx context.Context) ([]int32, error) {
var ret []int32

pids, err := callPsWithContext(ctx, "pid", 0, false)
pids, err := callPsWithContext(ctx, "pid", 0, false, false)
if err != nil {
return ret, err
}
Expand All @@ -55,7 +55,7 @@ func pidsWithContext(ctx context.Context) ([]int32, error) {
}

func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
r, err := callPsWithContext(ctx, "ppid", p.Pid, false)
r, err := callPsWithContext(ctx, "ppid", p.Pid, false, false)
if err != nil {
return 0, err
}
Expand All @@ -76,16 +76,16 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
name := common.IntToString(k.Proc.P_comm[:])

if len(name) >= 15 {
cmdlineSlice, err := p.CmdlineSliceWithContext(ctx)
cmdName, err := p.CmdNameWithContext(ctx)
if err != nil {
return "", err
}
if len(cmdlineSlice) > 0 {
extendedName := filepath.Base(cmdlineSlice[0])
if len(cmdName) > 0 {
extendedName := filepath.Base(cmdName[0])
if strings.HasPrefix(extendedName, p.name) {
name = extendedName
} else {
name = cmdlineSlice[0]
name = cmdName[0]
}
}
}
Expand All @@ -94,28 +94,37 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
}

func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
r, err := callPsWithContext(ctx, "command", p.Pid, false)
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
if err != nil {
return "", err
}
return strings.Join(r[0], " "), err
}

// CmdNameWithContext returns the command name (including spaces) without any arguments
func (p *Process) CmdNameWithContext(ctx context.Context) ([]string, error) {
r, err := callPsWithContext(ctx, "command", p.Pid, false, true)
if err != nil {
return nil, err
}
return r[0], err
}

// CmdlineSliceWithContext returns the command line arguments of the process as a slice with each
// element being an argument. Because of current deficiencies in the way that the command
// line arguments are found, single arguments that have spaces in the will actually be
// reported as two separate items. In order to do something better CGO would be needed
// to use the native darwin functions.
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
r, err := callPsWithContext(ctx, "command", p.Pid, false)
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
if err != nil {
return nil, err
}
return r[0], err
}

func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
r, err := callPsWithContext(ctx, "etime", p.Pid, false)
r, err := callPsWithContext(ctx, "etime", p.Pid, false, false)
if err != nil {
return 0, err
}
Expand Down Expand Up @@ -163,7 +172,7 @@ func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
}

func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {
r, err := callPsWithContext(ctx, "state", p.Pid, false)
r, err := callPsWithContext(ctx, "state", p.Pid, false, false)
if err != nil {
return []string{""}, err
}
Expand Down Expand Up @@ -255,7 +264,7 @@ func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, e
}

func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true)
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true, false)
if err != nil {
return 0, err
}
Expand Down Expand Up @@ -309,7 +318,7 @@ func convertCPUTimes(s string) (ret float64, err error) {
}

func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false)
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false, false)

if err != nil {
return nil, err
Expand All @@ -333,7 +342,7 @@ func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error)
}

func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false)
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false, false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -421,7 +430,7 @@ func (p *Process) getKProc() (*KinfoProc, error) {
// Return value deletes Header line(you must not input wrong arg).
// And splited by Space. Caller have responsibility to manage.
// If passed arg pid is 0, get information from all process.
func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption bool) ([][]string, error) {
func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption bool, nameOption bool) ([][]string, error) {
bin, err := exec.LookPath("ps")
if err != nil {
return [][]string{}, err
Expand All @@ -435,6 +444,9 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption
} else {
cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))}
}
if nameOption {
cmd = append(cmd, "-c")
}
out, err := invoke.CommandWithContext(ctx, bin, cmd...)
if err != nil {
return [][]string{}, err
Expand All @@ -444,11 +456,15 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption
var ret [][]string
for _, l := range lines[1:] {
var lr []string
for _, r := range strings.Split(l, " ") {
if r == "" {
continue
if nameOption {
lr = append(lr, l)
} else {
for _, r := range strings.Split(l, " ") {
if r == "" {
continue
}
lr = append(lr, strings.TrimSpace(r))
}
lr = append(lr, strings.TrimSpace(r))
}
if len(lr) != 0 {
ret = append(ret, lr)
Expand Down
Loading

0 comments on commit e8b2bea

Please sign in to comment.