2016-11-08 03:29:14 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2016-11-14 06:38:17 +02:00
|
|
|
"errors"
|
2016-11-08 03:29:14 +02:00
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net"
|
2016-11-14 06:04:40 +02:00
|
|
|
"os"
|
2016-11-14 06:38:17 +02:00
|
|
|
"os/exec"
|
2016-11-14 06:04:40 +02:00
|
|
|
|
2016-11-08 03:29:14 +02:00
|
|
|
"github.com/google/gops/signal"
|
|
|
|
)
|
|
|
|
|
|
|
|
var cmds = map[string](func(pid int) error){
|
|
|
|
"stack": stackTrace,
|
|
|
|
"gc": gc,
|
|
|
|
"memstats": memStats,
|
|
|
|
"version": version,
|
2016-11-14 06:04:40 +02:00
|
|
|
"pprof": pprof,
|
2016-11-08 03:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func stackTrace(pid int) error {
|
|
|
|
out, err := cmd(pid, signal.StackTrace)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
fmt.Println(out)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func gc(pid int) error {
|
|
|
|
_, err := cmd(pid, signal.GC)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func memStats(pid int) error {
|
|
|
|
out, err := cmd(pid, signal.MemStats)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
fmt.Printf(out)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func version(pid int) error {
|
|
|
|
out, err := cmd(pid, signal.Version)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
fmt.Printf(out)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-11-14 06:04:40 +02:00
|
|
|
func pprof(pid int) error {
|
2016-11-14 06:38:17 +02:00
|
|
|
var s byte
|
|
|
|
switch {
|
|
|
|
case *cpu:
|
|
|
|
fmt.Println("Profiling CPU now, will take 30 secs...")
|
|
|
|
s = signal.CPUProfile
|
|
|
|
case *heap:
|
|
|
|
s = signal.HeapProfile
|
|
|
|
default:
|
|
|
|
return errors.New("unknown pprof profile")
|
|
|
|
}
|
|
|
|
out, err := cmd(pid, s)
|
2016-11-14 06:04:40 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-11-14 06:38:17 +02:00
|
|
|
if out == "" {
|
|
|
|
return errors.New("failed to read the profile")
|
|
|
|
}
|
2016-11-14 06:04:40 +02:00
|
|
|
tmpfile, err := ioutil.TempFile("", "heap-profile")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-11-14 06:38:17 +02:00
|
|
|
defer os.Remove(tmpfile.Name())
|
|
|
|
|
2016-11-14 06:04:40 +02:00
|
|
|
if err := ioutil.WriteFile(tmpfile.Name(), []byte(out), 0); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-11-14 07:02:29 +02:00
|
|
|
// TODO(jbd): pass binary as an arg to symbolize the profile.
|
2016-11-14 06:04:40 +02:00
|
|
|
cmd := exec.Command("go", "tool", "pprof", tmpfile.Name())
|
|
|
|
cmd.Env = os.Environ()
|
|
|
|
cmd.Stdin = os.Stdin
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
return cmd.Run()
|
|
|
|
}
|
|
|
|
|
2016-11-08 03:29:14 +02:00
|
|
|
func cmd(pid int, c byte) (string, error) {
|
|
|
|
sock := fmt.Sprintf("/tmp/gops%d.sock", pid)
|
|
|
|
conn, err := net.Dial("unix", sock)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if _, err := conn.Write([]byte{c}); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
all, err := ioutil.ReadAll(conn)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(all), nil
|
|
|
|
}
|