Skip to content

Commit

Permalink
goprocess: replace go-ps with gopsutil (#187)
Browse files Browse the repository at this point in the history
The keybase's fork of go-ps seems abandoned, and gopsutil is already
introduced with same functionalities.
  • Loading branch information
zhsj authored Nov 25, 2022
1 parent 4e8ba58 commit eebecad
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 40 deletions.
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/google/gops
go 1.13

require (
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19
github.com/shirou/gopsutil/v3 v3.22.10
github.com/spf13/cobra v1.6.1
github.com/xlab/treeprint v1.1.0
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19 h1:WjT3fLi9n8YWh/Ih8Q1LHAPsTqGddPcHqscN+PJ3i68=
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
51 changes: 34 additions & 17 deletions goprocess/goprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"sync"

"github.com/google/gops/internal"
ps "github.com/keybase/go-ps"
"github.com/shirou/gopsutil/v3/process"
)

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

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

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

for _, ps := range pss {
Expand All @@ -63,10 +63,19 @@ func findAll(pss []ps.Process, isGo isGoFunc, concurrencyLimit int) []P {
if !ok {
continue
}
ppid, err := pr.Ppid()
if err != nil {
continue
}
name, err := pr.Name()
if err != nil {
continue
}

output <- P{
PID: pr.Pid(),
PPID: pr.PPid(),
Exec: pr.Executable(),
PID: int(pr.Pid),
PPID: int(ppid),
Exec: name,
Path: path,
BuildVersion: version,
Agent: agent,
Expand All @@ -86,18 +95,26 @@ func findAll(pss []ps.Process, isGo isGoFunc, concurrencyLimit int) []P {

// Find finds info about the process identified with the given PID.
func Find(pid int) (p P, ok bool, err error) {
pr, err := ps.FindProcess(pid)
pr, err := process.NewProcess(int32(pid))
if err != nil {
return P{}, false, err
}
path, version, agent, ok, err := isGo(pr)
if !ok {
if !ok || err != nil {
return P{}, false, nil
}
ppid, err := pr.Ppid()
if err != nil {
return P{}, false, err
}
name, err := pr.Name()
if err != nil {
return P{}, false, err
}
return P{
PID: pr.Pid(),
PPID: pr.PPid(),
Exec: pr.Executable(),
PID: int(pr.Pid),
PPID: int(ppid),
Exec: name,
Path: path,
BuildVersion: version,
Agent: agent,
Expand All @@ -108,12 +125,12 @@ func Find(pid int) (p P, ok bool, err error) {
// in the process' binary and determines if the process
// if a Go process or not. If the process is a Go process,
// it reports PID, binary name and full path of the binary.
func isGo(pr ps.Process) (path, version string, agent, ok bool, err error) {
if pr.Pid() == 0 {
func isGo(pr *process.Process) (path, version string, agent, ok bool, err error) {
if pr.Pid == 0 {
// ignore system process
return
}
path, err = pr.Path()
path, err = pr.Exe()
if err != nil {
return
}
Expand All @@ -122,7 +139,7 @@ func isGo(pr ps.Process) (path, version string, agent, ok bool, err error) {
return
}
ok = true
pidfile, err := internal.PIDFile(pr.Pid())
pidfile, err := internal.PIDFile(int(pr.Pid))
if err == nil {
_, err := os.Stat(pidfile)
agent = err == nil
Expand Down
74 changes: 54 additions & 20 deletions goprocess/goprocess_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package goprocess

import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"runtime"
"sort"
"strconv"
"testing"

"github.com/keybase/go-ps"
"github.com/shirou/gopsutil/v3/process"
)

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

// TestFindAll tests findAll implementation function.
func TestFindAll(t *testing.T) {
testProcess, err := process.NewProcess(int32(os.Getpid()))
if err != nil {
t.Errorf("failed to get current process: %v", err)
}
testPpid, _ := testProcess.Ppid()
testExec, _ := testProcess.Name()
wantProcess := P{PID: int(testProcess.Pid), PPID: int(testPpid), Exec: testExec}

for _, tc := range []struct {
name string
concurrencyLimit int
input []ps.Process
input []*process.Process
goPIDs []int
want []P
mock bool
}{{
name: "no processes",
concurrencyLimit: 10,
Expand All @@ -30,28 +44,57 @@ func TestFindAll(t *testing.T) {
}, {
name: "non-Go process",
concurrencyLimit: 10,
input: fakeProcessesWithPIDs(1),
input: []*process.Process{testProcess},
want: nil,
}, {
name: "Go process",
concurrencyLimit: 10,
input: fakeProcessesWithPIDs(1),
goPIDs: []int{1},
want: []P{{PID: 1}},
input: []*process.Process{testProcess},
goPIDs: []int{int(testProcess.Pid)},
want: []P{wantProcess},
}, {
name: "filters Go processes",
concurrencyLimit: 10,
input: fakeProcessesWithPIDs(1, 2, 3, 4, 5, 6, 7),
goPIDs: []int{1, 3, 5, 7},
want: []P{{PID: 1}, {PID: 3}, {PID: 5}, {PID: 7}},
mock: true,
}, {
name: "Go processes above max concurrency (issue #123)",
concurrencyLimit: 2,
input: fakeProcessesWithPIDs(1, 2, 3, 4, 5, 6, 7),
goPIDs: []int{1, 3, 5, 7},
want: []P{{PID: 1}, {PID: 3}, {PID: 5}, {PID: 7}},
mock: true,
}} {
t.Run(tc.name, func(t *testing.T) {
if tc.mock {
if runtime.GOOS != "linux" {
t.Skip()
}
tempDir, err := ioutil.TempDir("", "")
if err != nil {
t.Errorf("failed to create temp dir: %v", err)
}
defer os.RemoveAll(tempDir)
for _, p := range tc.input {
os.Mkdir(filepath.Join(tempDir, strconv.Itoa(int(p.Pid))), 0o755)
ioutil.WriteFile(filepath.Join(tempDir, strconv.Itoa(int(p.Pid)), "stat"), []byte(
`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`,
), 0o644)
ioutil.WriteFile(filepath.Join(tempDir, strconv.Itoa(int(p.Pid)), "status"), []byte(
`Name:
Umask: 0022
State: R (running)
Tgid: 1440366
Ngid: 0
Pid: 1440366
PPid: 0
`,
), 0o644)
}
os.Setenv("HOST_PROC", tempDir)
}
actual := findAll(tc.input, fakeIsGo(tc.goPIDs), tc.concurrencyLimit)
sort.Slice(actual, func(i, j int) bool { return actual[i].PID < actual[j].PID })
if !reflect.DeepEqual(actual, tc.want) {
Expand All @@ -63,9 +106,9 @@ func TestFindAll(t *testing.T) {
}

func fakeIsGo(goPIDs []int) isGoFunc {
return func(pr ps.Process) (path, version string, agent, ok bool, err error) {
return func(pr *process.Process) (path, version string, agent, ok bool, err error) {
for _, p := range goPIDs {
if p == pr.Pid() {
if p == int(pr.Pid) {
ok = true
return
}
Expand All @@ -74,19 +117,10 @@ func fakeIsGo(goPIDs []int) isGoFunc {
}
}

func fakeProcessesWithPIDs(pids ...int) []ps.Process {
p := make([]ps.Process, 0, len(pids))
func fakeProcessesWithPIDs(pids ...int) []*process.Process {
p := make([]*process.Process, 0, len(pids))
for _, pid := range pids {
p = append(p, fakeProcess{pid: pid})
p = append(p, &process.Process{Pid: int32(pid)})
}
return p
}

type fakeProcess struct {
ps.Process
pid int
}

func (p fakeProcess) Pid() int { return p.pid }
func (p fakeProcess) PPid() int { return 0 }
func (p fakeProcess) Executable() string { return "" }

0 comments on commit eebecad

Please sign in to comment.