mirror of
https://github.com/shirou/gopsutil.git
synced 2025-04-26 13:48:59 +08:00
Fix spaces on long process names for MacOS
This commit is contained in:
parent
3585d276bc
commit
e8b2bea47f
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,20 +94,29 @@ 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
|
||||
}
|
||||
@ -115,7 +124,7 @@ func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,20 +94,29 @@ 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
|
||||
}
|
||||
@ -115,7 +124,7 @@ func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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)
|
||||
|
@ -315,6 +315,51 @@ 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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user