Skip to content

Commit 79048cc

Browse files
authored
Merge pull request #1035 from jblesener/fixmacosspaces
Fix spaces on long process names for MacOS
2 parents 76779af + 07797b1 commit 79048cc

File tree

4 files changed

+163
-37
lines changed

4 files changed

+163
-37
lines changed

process/process_darwin.go

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type _Ctype_struct___0 struct {
3838
func pidsWithContext(ctx context.Context) ([]int32, error) {
3939
var ret []int32
4040

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

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

7878
if len(name) >= 15 {
79-
cmdlineSlice, err := p.CmdlineSliceWithContext(ctx)
79+
cmdName, err := p.cmdNameWithContext(ctx)
8080
if err != nil {
8181
return "", err
8282
}
83-
if len(cmdlineSlice) > 0 {
84-
extendedName := filepath.Base(cmdlineSlice[0])
83+
if len(cmdName) > 0 {
84+
extendedName := filepath.Base(cmdName[0])
8585
if strings.HasPrefix(extendedName, p.name) {
8686
name = extendedName
8787
} else {
88-
name = cmdlineSlice[0]
88+
name = cmdName[0]
8989
}
9090
}
9191
}
@@ -94,28 +94,37 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
9494
}
9595

9696
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
97-
r, err := callPsWithContext(ctx, "command", p.Pid, false)
97+
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
9898
if err != nil {
9999
return "", err
100100
}
101101
return strings.Join(r[0], " "), err
102102
}
103103

104+
// cmdNameWithContext returns the command name (including spaces) without any arguments
105+
func (p *Process) cmdNameWithContext(ctx context.Context) ([]string, error) {
106+
r, err := callPsWithContext(ctx, "command", p.Pid, false, true)
107+
if err != nil {
108+
return nil, err
109+
}
110+
return r[0], err
111+
}
112+
104113
// CmdlineSliceWithContext returns the command line arguments of the process as a slice with each
105114
// element being an argument. Because of current deficiencies in the way that the command
106115
// line arguments are found, single arguments that have spaces in the will actually be
107116
// reported as two separate items. In order to do something better CGO would be needed
108117
// to use the native darwin functions.
109118
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
110-
r, err := callPsWithContext(ctx, "command", p.Pid, false)
119+
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
111120
if err != nil {
112121
return nil, err
113122
}
114123
return r[0], err
115124
}
116125

117126
func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
118-
r, err := callPsWithContext(ctx, "etime", p.Pid, false)
127+
r, err := callPsWithContext(ctx, "etime", p.Pid, false, false)
119128
if err != nil {
120129
return 0, err
121130
}
@@ -163,7 +172,7 @@ func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
163172
}
164173

165174
func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
166-
r, err := callPsWithContext(ctx, "state", p.Pid, false)
175+
r, err := callPsWithContext(ctx, "state", p.Pid, false, false)
167176
if err != nil {
168177
return "", err
169178
}
@@ -255,7 +264,7 @@ func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, e
255264
}
256265

257266
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
258-
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true)
267+
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true, false)
259268
if err != nil {
260269
return 0, err
261270
}
@@ -309,7 +318,7 @@ func convertCPUTimes(s string) (ret float64, err error) {
309318
}
310319

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

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

335344
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
336-
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false)
345+
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false, false)
337346
if err != nil {
338347
return nil, err
339348
}
@@ -419,9 +428,9 @@ func (p *Process) getKProc() (*KinfoProc, error) {
419428

420429
// call ps command.
421430
// Return value deletes Header line(you must not input wrong arg).
422-
// And splited by Space. Caller have responsibility to manage.
431+
// And split by space. Caller have responsibility to manage.
423432
// If passed arg pid is 0, get information from all process.
424-
func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption bool) ([][]string, error) {
433+
func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption bool, nameOption bool) ([][]string, error) {
425434
bin, err := exec.LookPath("ps")
426435
if err != nil {
427436
return [][]string{}, err
@@ -435,6 +444,10 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption
435444
} else {
436445
cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))}
437446
}
447+
448+
if nameOption {
449+
cmd = append(cmd, "-c")
450+
}
438451
out, err := invoke.CommandWithContext(ctx, bin, cmd...)
439452
if err != nil {
440453
return [][]string{}, err
@@ -443,13 +456,19 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption
443456

444457
var ret [][]string
445458
for _, l := range lines[1:] {
459+
446460
var lr []string
447-
for _, r := range strings.Split(l, " ") {
448-
if r == "" {
449-
continue
461+
if nameOption {
462+
lr = append(lr, l)
463+
} else {
464+
for _, r := range strings.Split(l, " ") {
465+
if r == "" {
466+
continue
467+
}
468+
lr = append(lr, strings.TrimSpace(r))
450469
}
451-
lr = append(lr, strings.TrimSpace(r))
452470
}
471+
453472
if len(lr) != 0 {
454473
ret = append(ret, lr)
455474
}

process/process_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,52 @@ func Test_Process_Name(t *testing.T) {
312312
t.Errorf("invalid Exe %s", n)
313313
}
314314
}
315+
316+
func Test_Process_Long_Name_With_Spaces(t *testing.T) {
317+
tmpdir, err := ioutil.TempDir("", "")
318+
if err != nil {
319+
t.Fatalf("unable to create temp dir %v", err)
320+
}
321+
defer os.RemoveAll(tmpdir) // clean up
322+
tmpfilepath := filepath.Join(tmpdir, "loooong name with spaces.go")
323+
tmpfile, err := os.Create(tmpfilepath)
324+
if err != nil {
325+
t.Fatalf("unable to create temp file %v", err)
326+
}
327+
328+
tmpfilecontent := []byte("package main\nimport(\n\"time\"\n)\nfunc main(){\nfor range time.Tick(time.Second) {}\n}")
329+
if _, err := tmpfile.Write(tmpfilecontent); err != nil {
330+
tmpfile.Close()
331+
t.Fatalf("unable to write temp file %v", err)
332+
}
333+
if err := tmpfile.Close(); err != nil {
334+
t.Fatalf("unable to close temp file %v", err)
335+
}
336+
337+
err = exec.Command("go", "build", "-o", tmpfile.Name()+".exe", tmpfile.Name()).Run()
338+
if err != nil {
339+
t.Fatalf("unable to build temp file %v", err)
340+
}
341+
342+
cmd := exec.Command(tmpfile.Name() + ".exe")
343+
344+
assert.Nil(t, cmd.Start())
345+
time.Sleep(100 * time.Millisecond)
346+
p, err := NewProcess(int32(cmd.Process.Pid))
347+
skipIfNotImplementedErr(t, err)
348+
assert.Nil(t, err)
349+
350+
n, err := p.Name()
351+
skipIfNotImplementedErr(t, err)
352+
if err != nil {
353+
t.Fatalf("getting name error %v", err)
354+
}
355+
basename := filepath.Base(tmpfile.Name() + ".exe")
356+
if basename != n {
357+
t.Fatalf("%s != %s", basename, n)
358+
}
359+
cmd.Process.Kill()
360+
}
315361
func Test_Process_Long_Name(t *testing.T) {
316362
tmpdir, err := ioutil.TempDir("", "")
317363
if err != nil {

v3/process/process_darwin.go

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type _Ctype_struct___0 struct {
3838
func pidsWithContext(ctx context.Context) ([]int32, error) {
3939
var ret []int32
4040

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

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

7878
if len(name) >= 15 {
79-
cmdlineSlice, err := p.CmdlineSliceWithContext(ctx)
79+
cmdName, err := p.cmdNameWithContext(ctx)
8080
if err != nil {
8181
return "", err
8282
}
83-
if len(cmdlineSlice) > 0 {
84-
extendedName := filepath.Base(cmdlineSlice[0])
83+
if len(cmdName) > 0 {
84+
extendedName := filepath.Base(cmdName[0])
8585
if strings.HasPrefix(extendedName, p.name) {
8686
name = extendedName
8787
} else {
88-
name = cmdlineSlice[0]
88+
name = cmdName[0]
8989
}
9090
}
9191
}
@@ -94,28 +94,37 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
9494
}
9595

9696
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
97-
r, err := callPsWithContext(ctx, "command", p.Pid, false)
97+
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
9898
if err != nil {
9999
return "", err
100100
}
101101
return strings.Join(r[0], " "), err
102102
}
103103

104+
// cmdNameWithContext returns the command name (including spaces) without any arguments
105+
func (p *Process) cmdNameWithContext(ctx context.Context) ([]string, error) {
106+
r, err := callPsWithContext(ctx, "command", p.Pid, false, true)
107+
if err != nil {
108+
return nil, err
109+
}
110+
return r[0], err
111+
}
112+
104113
// CmdlineSliceWithContext returns the command line arguments of the process as a slice with each
105114
// element being an argument. Because of current deficiencies in the way that the command
106115
// line arguments are found, single arguments that have spaces in the will actually be
107116
// reported as two separate items. In order to do something better CGO would be needed
108117
// to use the native darwin functions.
109118
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
110-
r, err := callPsWithContext(ctx, "command", p.Pid, false)
119+
r, err := callPsWithContext(ctx, "command", p.Pid, false, false)
111120
if err != nil {
112121
return nil, err
113122
}
114123
return r[0], err
115124
}
116125

117126
func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
118-
r, err := callPsWithContext(ctx, "etime", p.Pid, false)
127+
r, err := callPsWithContext(ctx, "etime", p.Pid, false, false)
119128
if err != nil {
120129
return 0, err
121130
}
@@ -163,7 +172,7 @@ func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
163172
}
164173

165174
func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {
166-
r, err := callPsWithContext(ctx, "state", p.Pid, false)
175+
r, err := callPsWithContext(ctx, "state", p.Pid, false, false)
167176
if err != nil {
168177
return []string{""}, err
169178
}
@@ -255,7 +264,7 @@ func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, e
255264
}
256265

257266
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
258-
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true)
267+
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true, false)
259268
if err != nil {
260269
return 0, err
261270
}
@@ -309,7 +318,7 @@ func convertCPUTimes(s string) (ret float64, err error) {
309318
}
310319

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

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

335344
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
336-
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false)
345+
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false, false)
337346
if err != nil {
338347
return nil, err
339348
}
@@ -421,7 +430,7 @@ func (p *Process) getKProc() (*KinfoProc, error) {
421430
// Return value deletes Header line(you must not input wrong arg).
422431
// And splited by Space. Caller have responsibility to manage.
423432
// If passed arg pid is 0, get information from all process.
424-
func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption bool) ([][]string, error) {
433+
func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption bool, nameOption bool) ([][]string, error) {
425434
bin, err := exec.LookPath("ps")
426435
if err != nil {
427436
return [][]string{}, err
@@ -435,6 +444,9 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption
435444
} else {
436445
cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))}
437446
}
447+
if nameOption {
448+
cmd = append(cmd, "-c")
449+
}
438450
out, err := invoke.CommandWithContext(ctx, bin, cmd...)
439451
if err != nil {
440452
return [][]string{}, err
@@ -444,11 +456,15 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption
444456
var ret [][]string
445457
for _, l := range lines[1:] {
446458
var lr []string
447-
for _, r := range strings.Split(l, " ") {
448-
if r == "" {
449-
continue
459+
if nameOption {
460+
lr = append(lr, l)
461+
} else {
462+
for _, r := range strings.Split(l, " ") {
463+
if r == "" {
464+
continue
465+
}
466+
lr = append(lr, strings.TrimSpace(r))
450467
}
451-
lr = append(lr, strings.TrimSpace(r))
452468
}
453469
if len(lr) != 0 {
454470
ret = append(ret, lr)

0 commit comments

Comments
 (0)