Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 "" }