2016-11-04 07:43:04 +02:00
|
|
|
// Copyright 2016 The Go Authors. All rights reserved.
|
2016-11-04 01:56:19 +02:00
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// Program gops is a tool to list currently running Go processes.
|
2016-11-03 23:01:55 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2016-11-04 07:32:46 +02:00
|
|
|
"flag"
|
2016-11-03 23:01:55 +02:00
|
|
|
"fmt"
|
|
|
|
"log"
|
2016-11-04 07:32:46 +02:00
|
|
|
"os"
|
2016-11-14 09:26:37 +02:00
|
|
|
"strings"
|
2016-11-03 23:01:55 +02:00
|
|
|
|
2016-11-05 22:03:31 +02:00
|
|
|
"github.com/google/gops/internal/objfile"
|
2016-11-03 23:01:55 +02:00
|
|
|
|
|
|
|
ps "github.com/keybase/go-ps"
|
|
|
|
)
|
|
|
|
|
2016-11-04 07:32:46 +02:00
|
|
|
const helpText = `Usage: gops is a tool to list and diagnose Go processes.
|
2016-11-04 08:05:56 +02:00
|
|
|
|
2016-11-08 03:29:14 +02:00
|
|
|
gops Lists all Go processes currently running.
|
|
|
|
gops [cmd] -p=<pid> See the section below.
|
2016-11-04 07:32:46 +02:00
|
|
|
|
2016-11-14 07:59:09 +02:00
|
|
|
Commands:
|
|
|
|
gc Runs the garbage collector and blocks until successful.
|
|
|
|
|
|
|
|
stack Prints the stack trace.
|
|
|
|
memstats Prints the garbage collection stats.
|
|
|
|
version Prints the Go version used to build the program.
|
|
|
|
|
|
|
|
pprof-heap Reads the heap profile and launches "go tool pprof".
|
|
|
|
pprof-mem Reads the CPU profile and launches "go tool pprof".
|
|
|
|
|
|
|
|
help Prints this help text.
|
2016-11-04 08:05:56 +02:00
|
|
|
|
2016-11-08 03:29:14 +02:00
|
|
|
All commands require the agent running on the Go process.
|
2016-11-14 09:26:37 +02:00
|
|
|
Symbol "*" indicates the process runs the agent.`
|
2016-11-04 07:32:46 +02:00
|
|
|
|
2016-11-04 08:05:56 +02:00
|
|
|
// TODO(jbd): add link that explains the use of agent.
|
|
|
|
|
2016-11-03 23:01:55 +02:00
|
|
|
func main() {
|
2016-11-04 07:32:46 +02:00
|
|
|
if len(os.Args) < 2 {
|
2016-11-08 03:29:14 +02:00
|
|
|
processes()
|
2016-11-04 07:32:46 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-11-08 03:29:14 +02:00
|
|
|
cmd := os.Args[1]
|
2016-11-12 05:55:16 +02:00
|
|
|
if cmd == "help" {
|
|
|
|
usage("")
|
|
|
|
}
|
2016-11-08 03:29:14 +02:00
|
|
|
fn, ok := cmds[cmd]
|
|
|
|
if !ok {
|
|
|
|
usage("unknown subcommand")
|
2016-11-04 07:32:46 +02:00
|
|
|
}
|
2016-11-08 03:29:14 +02:00
|
|
|
|
2016-11-08 04:04:21 +02:00
|
|
|
var pid int
|
|
|
|
flag.IntVar(&pid, "p", -1, "")
|
2016-11-08 03:29:14 +02:00
|
|
|
flag.CommandLine.Parse(os.Args[2:])
|
2016-11-08 04:04:21 +02:00
|
|
|
if pid == -1 {
|
2016-11-08 03:29:14 +02:00
|
|
|
usage("missing -p=<pid> flag")
|
2016-11-04 07:32:46 +02:00
|
|
|
}
|
2016-11-08 03:29:14 +02:00
|
|
|
|
2016-11-08 04:04:21 +02:00
|
|
|
if err := fn(pid); err != nil {
|
2016-11-08 03:29:14 +02:00
|
|
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
|
|
|
os.Exit(1)
|
2016-11-04 07:32:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-08 03:29:14 +02:00
|
|
|
func processes() {
|
2016-11-03 23:01:55 +02:00
|
|
|
pss, err := ps.Processes()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
var undetermined int
|
|
|
|
for _, pr := range pss {
|
2016-11-12 14:26:51 +02:00
|
|
|
if pr.Pid() == 0 {
|
|
|
|
// ignore system process
|
|
|
|
continue
|
|
|
|
}
|
2016-11-14 09:26:37 +02:00
|
|
|
path, err := pr.Path()
|
2016-11-03 23:01:55 +02:00
|
|
|
if err != nil {
|
|
|
|
undetermined++
|
|
|
|
continue
|
|
|
|
}
|
2016-11-14 09:26:37 +02:00
|
|
|
ok, agent, err := isGo(pr.Pid(), path)
|
2016-11-03 23:01:55 +02:00
|
|
|
if err != nil {
|
2016-11-04 04:33:43 +02:00
|
|
|
// TODO(jbd): worth to report the number?
|
2016-11-03 23:01:55 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if ok {
|
2016-11-14 09:26:37 +02:00
|
|
|
fmt.Printf("%d", pr.Pid())
|
|
|
|
if agent {
|
|
|
|
fmt.Printf("*")
|
|
|
|
}
|
|
|
|
fmt.Printf("\t%v\t(%v)\n", pr.Executable(), path)
|
2016-11-03 23:01:55 +02:00
|
|
|
}
|
|
|
|
}
|
2016-11-04 01:56:19 +02:00
|
|
|
if undetermined > 0 {
|
2016-11-04 02:52:29 +02:00
|
|
|
fmt.Printf("\n%d processes left undetermined\n", undetermined)
|
2016-11-04 01:56:19 +02:00
|
|
|
}
|
2016-11-03 23:01:55 +02:00
|
|
|
}
|
|
|
|
|
2016-11-14 09:26:37 +02:00
|
|
|
func isGo(pid int, path string) (ok bool, agent bool, err error) {
|
|
|
|
obj, err := objfile.Open(path)
|
2016-11-03 23:01:55 +02:00
|
|
|
if err != nil {
|
2016-11-14 09:26:37 +02:00
|
|
|
return false, false, err
|
2016-11-03 23:01:55 +02:00
|
|
|
}
|
|
|
|
defer obj.Close()
|
|
|
|
|
|
|
|
symbols, err := obj.Symbols()
|
|
|
|
if err != nil {
|
2016-11-14 09:26:37 +02:00
|
|
|
return false, false, err
|
2016-11-03 23:01:55 +02:00
|
|
|
}
|
|
|
|
|
2016-11-05 03:37:04 +02:00
|
|
|
// TODO(jbd): find a faster way to determine Go programs.
|
2016-11-03 23:01:55 +02:00
|
|
|
for _, s := range symbols {
|
|
|
|
if s.Name == "runtime.buildVersion" {
|
2016-11-14 09:26:37 +02:00
|
|
|
ok = true
|
|
|
|
}
|
|
|
|
if strings.HasPrefix(s.Name, "github.com/google/gops/agent") {
|
|
|
|
agent = true
|
2016-11-03 23:01:55 +02:00
|
|
|
}
|
|
|
|
}
|
2016-11-14 09:26:37 +02:00
|
|
|
return ok, agent, nil
|
2016-11-03 23:01:55 +02:00
|
|
|
}
|
2016-11-04 07:32:46 +02:00
|
|
|
|
2016-11-08 03:29:14 +02:00
|
|
|
func usage(msg string) {
|
|
|
|
if msg != "" {
|
|
|
|
fmt.Printf("gops: %v\n", msg)
|
2016-11-04 07:32:46 +02:00
|
|
|
}
|
2016-11-08 03:29:14 +02:00
|
|
|
fmt.Fprintf(os.Stderr, "%v\n", helpText)
|
2016-11-04 09:55:16 +02:00
|
|
|
os.Exit(1)
|
2016-11-04 07:32:46 +02:00
|
|
|
}
|