1
0
mirror of https://github.com/google/gops.git synced 2024-11-24 08:22:25 +02:00
gops/cmd.go

184 lines
3.9 KiB
Go
Raw Normal View History

2016-11-08 03:29:14 +02:00
package main
import (
"fmt"
2017-01-20 23:09:45 +02:00
"io"
2016-11-08 03:29:14 +02:00
"io/ioutil"
"net"
2016-11-14 06:04:40 +02:00
"os"
2016-11-14 06:38:17 +02:00
"os/exec"
2017-03-02 21:51:14 +02:00
"strconv"
"strings"
2016-11-14 06:04:40 +02:00
2016-11-15 09:07:31 +02:00
"github.com/google/gops/internal"
2016-11-08 03:29:14 +02:00
"github.com/google/gops/signal"
2017-03-02 21:51:14 +02:00
"github.com/pkg/errors"
2016-11-08 03:29:14 +02:00
)
2017-03-02 21:51:14 +02:00
var cmds = map[string](func(addr net.TCPAddr) error){
2016-11-14 07:50:30 +02:00
"stack": stackTrace,
"gc": gc,
"memstats": memStats,
"version": version,
"pprof-heap": pprofHeap,
"pprof-cpu": pprofCPU,
2017-01-03 11:15:13 +02:00
"stats": stats,
2017-01-20 23:09:45 +02:00
"trace": trace,
2016-11-08 03:29:14 +02:00
}
2017-03-02 21:51:14 +02:00
func stackTrace(addr net.TCPAddr) error {
return cmdWithPrint(addr, signal.StackTrace)
2016-11-08 03:29:14 +02:00
}
2017-03-02 21:51:14 +02:00
func gc(addr net.TCPAddr) error {
_, err := cmd(addr, signal.GC)
2016-11-08 03:29:14 +02:00
return err
}
2017-03-02 21:51:14 +02:00
func memStats(addr net.TCPAddr) error {
return cmdWithPrint(addr, signal.MemStats)
2016-11-08 03:29:14 +02:00
}
2017-03-02 21:51:14 +02:00
func version(addr net.TCPAddr) error {
return cmdWithPrint(addr, signal.Version)
2016-11-08 03:29:14 +02:00
}
2017-03-02 21:51:14 +02:00
func pprofHeap(addr net.TCPAddr) error {
return pprof(addr, signal.HeapProfile)
2016-11-14 07:50:30 +02:00
}
2017-03-02 21:51:14 +02:00
func pprofCPU(addr net.TCPAddr) error {
2016-11-14 07:50:30 +02:00
fmt.Println("Profiling CPU now, will take 30 secs...")
2017-03-02 21:51:14 +02:00
return pprof(addr, signal.CPUProfile)
2016-11-14 07:50:30 +02:00
}
2017-03-02 21:51:14 +02:00
func trace(addr net.TCPAddr) error {
2017-01-20 23:09:45 +02:00
fmt.Println("Tracing now, will take 5 secs...")
2017-03-02 21:51:14 +02:00
out, err := cmd(addr, signal.Trace)
2017-01-20 23:09:45 +02:00
if err != nil {
return err
}
if len(out) == 0 {
return errors.New("nothing has traced")
}
tmpfile, err := ioutil.TempFile("", "trace")
if err != nil {
return err
}
defer os.Remove(tmpfile.Name())
if err := ioutil.WriteFile(tmpfile.Name(), out, 0); err != nil {
return err
}
cmd := exec.Command("go", "tool", "trace", tmpfile.Name())
cmd.Env = os.Environ()
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
2017-03-02 21:51:14 +02:00
func pprof(addr net.TCPAddr, p byte) error {
tmpDumpFile, err := ioutil.TempFile("", "profile")
2016-11-14 06:04:40 +02:00
if err != nil {
return err
}
2017-03-02 21:51:14 +02:00
{
out, err := cmd(addr, p)
if err != nil {
return err
}
if len(out) == 0 {
return errors.New("failed to read the profile")
}
defer os.Remove(tmpDumpFile.Name())
if err := ioutil.WriteFile(tmpDumpFile.Name(), out, 0); err != nil {
return err
}
}
// Download running binary
tmpBinFile, err := ioutil.TempFile("", "binary")
2016-11-14 07:46:10 +02:00
if err != nil {
return err
}
2017-03-02 21:51:14 +02:00
{
out, err := cmd(addr, signal.BinaryDump)
if err != nil {
return errors.New("couldn't retrieve running binary's dump")
}
if len(out) == 0 {
return errors.New("failed to read the binary")
}
defer os.Remove(tmpBinFile.Name())
if err := ioutil.WriteFile(tmpBinFile.Name(), out, 0); err != nil {
return err
}
}
cmd := exec.Command("go", "tool", "pprof", tmpBinFile.Name(), tmpDumpFile.Name())
2016-11-14 06:04:40 +02:00
cmd.Env = os.Environ()
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
2017-03-02 21:51:14 +02:00
func stats(addr net.TCPAddr) error {
return cmdWithPrint(addr, signal.Stats)
2016-11-28 20:45:02 +02:00
}
2017-03-02 21:51:14 +02:00
func cmdWithPrint(addr net.TCPAddr, c byte) error {
out, err := cmd(addr, c)
2016-11-14 21:07:06 +02:00
if err != nil {
return err
}
2017-01-03 10:56:20 +02:00
fmt.Printf("%s", out)
2016-11-14 21:07:06 +02:00
return nil
}
2017-03-02 21:51:14 +02:00
// targetToAddr tries to parse the target string, be it remote host:port
// or local process's PID.
func targetToAddr(target string) (*net.TCPAddr, error) {
if strings.Index(target, ":") != -1 {
// addr host:port passed
var err error
addr, err := net.ResolveTCPAddr("tcp", target)
if err != nil {
return nil, errors.Wrap(err, "couldn't parse dst address")
}
return addr, nil
}
// try to find port by pid then, connect to local
pid, err := strconv.Atoi(target)
2016-11-15 08:59:09 +02:00
if err != nil {
2017-03-02 21:51:14 +02:00
return nil, errors.Wrap(err, "couldn't parse PID")
}
port, err := internal.GetPort(pid)
addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:"+port)
return addr, nil
}
func cmd(addr net.TCPAddr, c byte) ([]byte, error) {
conn, err := cmdLazy(addr, c)
if err != nil {
return nil, errors.Wrap(err, "couldn't get port by PID")
2016-11-15 08:59:09 +02:00
}
2017-03-02 21:51:14 +02:00
2017-01-20 23:09:45 +02:00
all, err := ioutil.ReadAll(conn)
2016-11-08 03:29:14 +02:00
if err != nil {
2017-01-03 10:56:20 +02:00
return nil, err
2016-11-08 03:29:14 +02:00
}
2017-01-20 23:09:45 +02:00
return all, nil
}
2017-03-02 21:51:14 +02:00
func cmdLazy(addr net.TCPAddr, c byte) (io.Reader, error) {
conn, err := net.DialTCP("tcp", nil, &addr)
2016-11-08 03:29:14 +02:00
if err != nil {
2017-01-03 10:56:20 +02:00
return nil, err
2016-11-08 03:29:14 +02:00
}
2017-01-20 23:09:45 +02:00
if _, err := conn.Write([]byte{c}); err != nil {
return nil, err
}
return conn, nil
2016-11-08 03:29:14 +02:00
}