Skip to content

Commit

Permalink
Export Go process reporting section as a package.
Browse files Browse the repository at this point in the history
  • Loading branch information
rakyll committed Aug 29, 2017
1 parent 1128431 commit 5c0015f
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 53 deletions.
90 changes: 90 additions & 0 deletions goprocess/gp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package goprocess reports the Go processes running on a host.
package goprocess

import (
"os"
"sync"

"github.com/google/gops/internal"
"github.com/google/gops/internal/goversion"
ps "github.com/keybase/go-ps"
)

// GoProcess represents a Go process.
type GoProcess struct {
PID int
PPID int
Exec string
Path string
BuildVersion string
Agent bool
}

// Find returns all the Go processes currently
// running on this host.
func Find() []GoProcess {
var results []GoProcess

pss, err := ps.Processes()
if err != nil {
return results
}

var wg sync.WaitGroup
wg.Add(len(pss))

for _, pr := range pss {
pr := pr
go func() {
defer wg.Done()

path, version, agent, ok, err := isGo(pr)
if err != nil {
// TODO(jbd): Return a list of errors.
}
if !ok {
return
}
results = append(results, GoProcess{
PID: pr.Pid(),
PPID: pr.PPid(),
Exec: pr.Executable(),
Path: path,
BuildVersion: version,
Agent: agent,
})
}()
}
wg.Wait()
return results
}

// isGo looks up the runtime.buildVersion symbol
// 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 {
// ignore system process
return
}
path, err = pr.Path()
if err != nil {
return
}
fi, err := os.Stat(path)
if err != nil {
return
}
version, ok = goversion.Report(path, path, fi)
pidfile, err := internal.PIDFile(pr.Pid())
if err == nil {
_, err := os.Stat(pidfile)
agent = err == nil
}
return path, version, agent, ok, nil
}
58 changes: 5 additions & 53 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@ package main
import (
"bytes"
"fmt"
"log"
"os"
"sync"

"github.com/google/gops/internal"
"github.com/google/gops/internal/goversion"
ps "github.com/keybase/go-ps"
"github.com/google/gops/goprocess"
)

const helpText = `Usage: gops is a tool to list and diagnose Go processes.
Expand Down Expand Up @@ -68,57 +64,13 @@ func main() {
}

func processes() {
pss, err := ps.Processes()
if err != nil {
log.Fatal(err)
}

var wg sync.WaitGroup
wg.Add(len(pss))

for _, pr := range pss {
pr := pr
go func() {
defer wg.Done()

printIfGo(pr)
}()
}
wg.Wait()
}

// printIfGo looks up the runtime.buildVersion symbol
// 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 printIfGo(pr ps.Process) {
if pr.Pid() == 0 {
// ignore system process
return
}
path, err := pr.Path()
if err != nil {
return
}
fi, err := os.Stat(path)
if err != nil {
return
}
version, ok := goversion.Report(path, path, fi)
var agent bool
pidfile, err := internal.PIDFile(pr.Pid())
if err == nil {
_, err := os.Stat(pidfile)
agent = err == nil
}

if ok {
for _, p := range goprocess.Find() {
buf := bytes.NewBuffer(nil)
fmt.Fprintf(buf, "%d", pr.Pid())
if agent {
fmt.Fprintf(buf, "%d", p.PID)
if p.Agent {
fmt.Fprint(buf, "*")
}
fmt.Fprintf(buf, "\t%v\t%v\t%v\n", pr.Executable(), version, path)
fmt.Fprintf(buf, "\t%v\t%v\t%v\n", p.Exec, p.BuildVersion, p.Path)
buf.WriteTo(os.Stdout)
}
}
Expand Down

0 comments on commit 5c0015f

Please sign in to comment.