Skip to content

Commit eebecad

Browse files
authored
goprocess: replace go-ps with gopsutil (#187)
The keybase's fork of go-ps seems abandoned, and gopsutil is already introduced with same functionalities.
1 parent 4e8ba58 commit eebecad

File tree

4 files changed

+88
-40
lines changed

4 files changed

+88
-40
lines changed

go.mod

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ module github.com/google/gops
33
go 1.13
44

55
require (
6-
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19
76
github.com/shirou/gopsutil/v3 v3.22.10
87
github.com/spf13/cobra v1.6.1
98
github.com/xlab/treeprint v1.1.0

go.sum

-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
99
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
1010
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
1111
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
12-
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19 h1:WjT3fLi9n8YWh/Ih8Q1LHAPsTqGddPcHqscN+PJ3i68=
13-
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ=
1412
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
1513
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
1614
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

goprocess/goprocess.go

+34-17
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"sync"
1111

1212
"github.com/google/gops/internal"
13-
ps "github.com/keybase/go-ps"
13+
"github.com/shirou/gopsutil/v3/process"
1414
)
1515

1616
// P represents a Go process.
@@ -26,18 +26,18 @@ type P struct {
2626
// FindAll returns all the Go processes currently running on this host.
2727
func FindAll() []P {
2828
const concurrencyLimit = 10 // max number of concurrent workers
29-
pss, err := ps.Processes()
29+
pss, err := process.Processes()
3030
if err != nil {
3131
return nil
3232
}
3333
return findAll(pss, isGo, concurrencyLimit)
3434
}
3535

3636
// Allows to inject isGo for testing.
37-
type isGoFunc func(ps.Process) (path, version string, agent, ok bool, err error)
37+
type isGoFunc func(*process.Process) (path, version string, agent, ok bool, err error)
3838

39-
func findAll(pss []ps.Process, isGo isGoFunc, concurrencyLimit int) []P {
40-
input := make(chan ps.Process, len(pss))
39+
func findAll(pss []*process.Process, isGo isGoFunc, concurrencyLimit int) []P {
40+
input := make(chan *process.Process, len(pss))
4141
output := make(chan P, len(pss))
4242

4343
for _, ps := range pss {
@@ -63,10 +63,19 @@ func findAll(pss []ps.Process, isGo isGoFunc, concurrencyLimit int) []P {
6363
if !ok {
6464
continue
6565
}
66+
ppid, err := pr.Ppid()
67+
if err != nil {
68+
continue
69+
}
70+
name, err := pr.Name()
71+
if err != nil {
72+
continue
73+
}
74+
6675
output <- P{
67-
PID: pr.Pid(),
68-
PPID: pr.PPid(),
69-
Exec: pr.Executable(),
76+
PID: int(pr.Pid),
77+
PPID: int(ppid),
78+
Exec: name,
7079
Path: path,
7180
BuildVersion: version,
7281
Agent: agent,
@@ -86,18 +95,26 @@ func findAll(pss []ps.Process, isGo isGoFunc, concurrencyLimit int) []P {
8695

8796
// Find finds info about the process identified with the given PID.
8897
func Find(pid int) (p P, ok bool, err error) {
89-
pr, err := ps.FindProcess(pid)
98+
pr, err := process.NewProcess(int32(pid))
9099
if err != nil {
91100
return P{}, false, err
92101
}
93102
path, version, agent, ok, err := isGo(pr)
94-
if !ok {
103+
if !ok || err != nil {
95104
return P{}, false, nil
96105
}
106+
ppid, err := pr.Ppid()
107+
if err != nil {
108+
return P{}, false, err
109+
}
110+
name, err := pr.Name()
111+
if err != nil {
112+
return P{}, false, err
113+
}
97114
return P{
98-
PID: pr.Pid(),
99-
PPID: pr.PPid(),
100-
Exec: pr.Executable(),
115+
PID: int(pr.Pid),
116+
PPID: int(ppid),
117+
Exec: name,
101118
Path: path,
102119
BuildVersion: version,
103120
Agent: agent,
@@ -108,12 +125,12 @@ func Find(pid int) (p P, ok bool, err error) {
108125
// in the process' binary and determines if the process
109126
// if a Go process or not. If the process is a Go process,
110127
// it reports PID, binary name and full path of the binary.
111-
func isGo(pr ps.Process) (path, version string, agent, ok bool, err error) {
112-
if pr.Pid() == 0 {
128+
func isGo(pr *process.Process) (path, version string, agent, ok bool, err error) {
129+
if pr.Pid == 0 {
113130
// ignore system process
114131
return
115132
}
116-
path, err = pr.Path()
133+
path, err = pr.Exe()
117134
if err != nil {
118135
return
119136
}
@@ -122,7 +139,7 @@ func isGo(pr ps.Process) (path, version string, agent, ok bool, err error) {
122139
return
123140
}
124141
ok = true
125-
pidfile, err := internal.PIDFile(pr.Pid())
142+
pidfile, err := internal.PIDFile(int(pr.Pid))
126143
if err == nil {
127144
_, err := os.Stat(pidfile)
128145
agent = err == nil

goprocess/goprocess_test.go

+54-20
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package goprocess
22

33
import (
4+
"io/ioutil"
5+
"os"
6+
"path/filepath"
47
"reflect"
8+
"runtime"
59
"sort"
10+
"strconv"
611
"testing"
712

8-
"github.com/keybase/go-ps"
13+
"github.com/shirou/gopsutil/v3/process"
914
)
1015

1116
func BenchmarkFindAll(b *testing.B) {
@@ -16,12 +21,21 @@ func BenchmarkFindAll(b *testing.B) {
1621

1722
// TestFindAll tests findAll implementation function.
1823
func TestFindAll(t *testing.T) {
24+
testProcess, err := process.NewProcess(int32(os.Getpid()))
25+
if err != nil {
26+
t.Errorf("failed to get current process: %v", err)
27+
}
28+
testPpid, _ := testProcess.Ppid()
29+
testExec, _ := testProcess.Name()
30+
wantProcess := P{PID: int(testProcess.Pid), PPID: int(testPpid), Exec: testExec}
31+
1932
for _, tc := range []struct {
2033
name string
2134
concurrencyLimit int
22-
input []ps.Process
35+
input []*process.Process
2336
goPIDs []int
2437
want []P
38+
mock bool
2539
}{{
2640
name: "no processes",
2741
concurrencyLimit: 10,
@@ -30,28 +44,57 @@ func TestFindAll(t *testing.T) {
3044
}, {
3145
name: "non-Go process",
3246
concurrencyLimit: 10,
33-
input: fakeProcessesWithPIDs(1),
47+
input: []*process.Process{testProcess},
3448
want: nil,
3549
}, {
3650
name: "Go process",
3751
concurrencyLimit: 10,
38-
input: fakeProcessesWithPIDs(1),
39-
goPIDs: []int{1},
40-
want: []P{{PID: 1}},
52+
input: []*process.Process{testProcess},
53+
goPIDs: []int{int(testProcess.Pid)},
54+
want: []P{wantProcess},
4155
}, {
4256
name: "filters Go processes",
4357
concurrencyLimit: 10,
4458
input: fakeProcessesWithPIDs(1, 2, 3, 4, 5, 6, 7),
4559
goPIDs: []int{1, 3, 5, 7},
4660
want: []P{{PID: 1}, {PID: 3}, {PID: 5}, {PID: 7}},
61+
mock: true,
4762
}, {
4863
name: "Go processes above max concurrency (issue #123)",
4964
concurrencyLimit: 2,
5065
input: fakeProcessesWithPIDs(1, 2, 3, 4, 5, 6, 7),
5166
goPIDs: []int{1, 3, 5, 7},
5267
want: []P{{PID: 1}, {PID: 3}, {PID: 5}, {PID: 7}},
68+
mock: true,
5369
}} {
5470
t.Run(tc.name, func(t *testing.T) {
71+
if tc.mock {
72+
if runtime.GOOS != "linux" {
73+
t.Skip()
74+
}
75+
tempDir, err := ioutil.TempDir("", "")
76+
if err != nil {
77+
t.Errorf("failed to create temp dir: %v", err)
78+
}
79+
defer os.RemoveAll(tempDir)
80+
for _, p := range tc.input {
81+
os.Mkdir(filepath.Join(tempDir, strconv.Itoa(int(p.Pid))), 0o755)
82+
ioutil.WriteFile(filepath.Join(tempDir, strconv.Itoa(int(p.Pid)), "stat"), []byte(
83+
`1440024 () R 0 1440024 0 34821 1440024 4194304 134 0 0 0 0 0 0 0 20 0 1 0 95120609 6746112 274 18446744073709551615 94467689938944 94467690036601 140724224197808 0 0 0 0 0 0 0 0 0 17 11 0 0 0 0 0 94467690068048 94467690071296 94467715629056 140724224199226 140724224199259 140724224199259 140724224204780 0`,
84+
), 0o644)
85+
ioutil.WriteFile(filepath.Join(tempDir, strconv.Itoa(int(p.Pid)), "status"), []byte(
86+
`Name:
87+
Umask: 0022
88+
State: R (running)
89+
Tgid: 1440366
90+
Ngid: 0
91+
Pid: 1440366
92+
PPid: 0
93+
`,
94+
), 0o644)
95+
}
96+
os.Setenv("HOST_PROC", tempDir)
97+
}
5598
actual := findAll(tc.input, fakeIsGo(tc.goPIDs), tc.concurrencyLimit)
5699
sort.Slice(actual, func(i, j int) bool { return actual[i].PID < actual[j].PID })
57100
if !reflect.DeepEqual(actual, tc.want) {
@@ -63,9 +106,9 @@ func TestFindAll(t *testing.T) {
63106
}
64107

65108
func fakeIsGo(goPIDs []int) isGoFunc {
66-
return func(pr ps.Process) (path, version string, agent, ok bool, err error) {
109+
return func(pr *process.Process) (path, version string, agent, ok bool, err error) {
67110
for _, p := range goPIDs {
68-
if p == pr.Pid() {
111+
if p == int(pr.Pid) {
69112
ok = true
70113
return
71114
}
@@ -74,19 +117,10 @@ func fakeIsGo(goPIDs []int) isGoFunc {
74117
}
75118
}
76119

77-
func fakeProcessesWithPIDs(pids ...int) []ps.Process {
78-
p := make([]ps.Process, 0, len(pids))
120+
func fakeProcessesWithPIDs(pids ...int) []*process.Process {
121+
p := make([]*process.Process, 0, len(pids))
79122
for _, pid := range pids {
80-
p = append(p, fakeProcess{pid: pid})
123+
p = append(p, &process.Process{Pid: int32(pid)})
81124
}
82125
return p
83126
}
84-
85-
type fakeProcess struct {
86-
ps.Process
87-
pid int
88-
}
89-
90-
func (p fakeProcess) Pid() int { return p.pid }
91-
func (p fakeProcess) PPid() int { return 0 }
92-
func (p fakeProcess) Executable() string { return "" }

0 commit comments

Comments
 (0)