2016-11-04 07:43:04 +02:00
|
|
|
// Copyright 2016 The Go Authors. All rights reserved.
|
2016-11-04 05:59:53 +02:00
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// Package agent provides hooks programs can register to retrieve
|
|
|
|
// diagnostics data by using gops.
|
|
|
|
package agent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"os"
|
2016-11-04 09:54:41 +02:00
|
|
|
gosignal "os/signal"
|
2016-11-04 05:59:53 +02:00
|
|
|
"runtime"
|
2016-11-14 06:04:40 +02:00
|
|
|
"runtime/pprof"
|
2016-11-14 06:38:17 +02:00
|
|
|
"time"
|
2016-11-04 09:54:41 +02:00
|
|
|
|
2016-11-05 22:03:31 +02:00
|
|
|
"github.com/google/gops/signal"
|
2016-11-04 05:59:53 +02:00
|
|
|
)
|
|
|
|
|
2016-11-07 23:05:46 +02:00
|
|
|
// Start stars the gops agent on a host process. Once agent started,
|
|
|
|
// users can use the advanced gops features.
|
2016-11-12 03:05:41 +02:00
|
|
|
//
|
|
|
|
// Note: The agent exposes an endpoint via a UNIX socket that can be used by
|
|
|
|
// any program on the system. Review your security requirements before
|
|
|
|
// starting the agent.
|
2016-11-07 23:05:46 +02:00
|
|
|
func Start() error {
|
2016-11-06 08:45:55 +02:00
|
|
|
// TODO(jbd): Expose these endpoints on HTTP. Then, we can enable
|
|
|
|
// the agent on Windows systems.
|
2016-11-04 05:59:53 +02:00
|
|
|
sock := fmt.Sprintf("/tmp/gops%d.sock", os.Getpid())
|
|
|
|
l, err := net.Listen("unix", sock)
|
|
|
|
if err != nil {
|
2016-11-07 23:01:35 +02:00
|
|
|
return err
|
2016-11-04 05:59:53 +02:00
|
|
|
}
|
2016-11-04 09:54:41 +02:00
|
|
|
|
|
|
|
c := make(chan os.Signal, 1)
|
|
|
|
gosignal.Notify(c, os.Interrupt)
|
|
|
|
go func() {
|
|
|
|
// cleanup the socket on shutdown.
|
|
|
|
<-c
|
|
|
|
os.Remove(sock)
|
|
|
|
os.Exit(1)
|
|
|
|
}()
|
|
|
|
|
2016-11-04 05:59:53 +02:00
|
|
|
go func() {
|
|
|
|
buf := make([]byte, 1)
|
|
|
|
for {
|
|
|
|
fd, err := l.Accept()
|
|
|
|
if err != nil {
|
2016-11-04 08:10:27 +02:00
|
|
|
fmt.Fprintf(os.Stderr, "gops: %v", err)
|
2016-11-04 05:59:53 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if _, err := fd.Read(buf); err != nil {
|
2016-11-04 08:10:27 +02:00
|
|
|
fmt.Fprintf(os.Stderr, "gops: %v", err)
|
2016-11-04 05:59:53 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err := handle(fd, buf); err != nil {
|
2016-11-04 08:10:27 +02:00
|
|
|
fmt.Fprintf(os.Stderr, "gops: %v", err)
|
2016-11-04 05:59:53 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
fd.Close()
|
|
|
|
}
|
|
|
|
}()
|
2016-11-07 23:01:35 +02:00
|
|
|
return err
|
2016-11-04 05:59:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func handle(conn net.Conn, msg []byte) error {
|
|
|
|
switch msg[0] {
|
2016-11-06 08:46:11 +02:00
|
|
|
case signal.StackTrace:
|
2016-11-04 05:59:53 +02:00
|
|
|
buf := make([]byte, 1<<16)
|
|
|
|
n := runtime.Stack(buf, true)
|
|
|
|
_, err := conn.Write(buf[:n])
|
|
|
|
return err
|
2016-11-04 07:32:46 +02:00
|
|
|
case signal.GC:
|
2016-11-04 06:08:37 +02:00
|
|
|
runtime.GC()
|
|
|
|
_, err := conn.Write([]byte("ok"))
|
|
|
|
return err
|
2016-11-04 08:28:51 +02:00
|
|
|
case signal.MemStats:
|
|
|
|
var s runtime.MemStats
|
|
|
|
runtime.ReadMemStats(&s)
|
2016-11-14 05:20:53 +02:00
|
|
|
fmt.Fprintf(conn, "alloc: %v bytes\n", s.Alloc)
|
|
|
|
fmt.Fprintf(conn, "total-alloc: %v bytes\n", s.TotalAlloc)
|
|
|
|
fmt.Fprintf(conn, "sys: %v bytes\n", s.Sys)
|
2016-11-04 08:28:51 +02:00
|
|
|
fmt.Fprintf(conn, "lookups: %v\n", s.Lookups)
|
|
|
|
fmt.Fprintf(conn, "mallocs: %v\n", s.Mallocs)
|
|
|
|
fmt.Fprintf(conn, "frees: %v\n", s.Frees)
|
2016-11-14 05:20:53 +02:00
|
|
|
fmt.Fprintf(conn, "heap-alloc: %v bytes\n", s.HeapAlloc)
|
|
|
|
fmt.Fprintf(conn, "heap-sys: %v bytes\n", s.HeapSys)
|
|
|
|
fmt.Fprintf(conn, "heap-idle: %v bytes\n", s.HeapIdle)
|
|
|
|
fmt.Fprintf(conn, "heap-in-use: %v bytes\n", s.HeapInuse)
|
|
|
|
fmt.Fprintf(conn, "heap-released: %v bytes\n", s.HeapReleased)
|
2016-11-04 08:28:51 +02:00
|
|
|
fmt.Fprintf(conn, "heap-objects: %v\n", s.HeapObjects)
|
2016-11-14 05:20:53 +02:00
|
|
|
fmt.Fprintf(conn, "stack-in-use: %v bytes\n", s.StackInuse)
|
|
|
|
fmt.Fprintf(conn, "stack-sys: %v bytes\n", s.StackSys)
|
|
|
|
fmt.Fprintf(conn, "next-gc: when heap-alloc >= %v bytes\n", s.NextGC)
|
2016-11-04 08:28:51 +02:00
|
|
|
fmt.Fprintf(conn, "last-gc: %v ns ago\n", s.LastGC)
|
|
|
|
fmt.Fprintf(conn, "gc-pause: %v ns\n", s.PauseTotalNs)
|
|
|
|
fmt.Fprintf(conn, "num-gc: %v\n", s.NumGC)
|
|
|
|
fmt.Fprintf(conn, "enable-gc: %v\n", s.EnableGC)
|
|
|
|
fmt.Fprintf(conn, "debug-gc: %v\n", s.DebugGC)
|
2016-11-04 07:53:07 +02:00
|
|
|
case signal.Version:
|
|
|
|
fmt.Fprintf(conn, "%v\n", runtime.Version())
|
2016-11-14 06:04:40 +02:00
|
|
|
case signal.HeapProfile:
|
2016-11-14 07:00:00 +02:00
|
|
|
pprof.Lookup("heap").WriteTo(conn, 0)
|
2016-11-14 06:04:40 +02:00
|
|
|
case signal.CPUProfile:
|
2016-11-14 06:38:17 +02:00
|
|
|
if err := pprof.StartCPUProfile(conn); err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
time.Sleep(30 * time.Second)
|
|
|
|
pprof.StopCPUProfile()
|
2016-11-04 05:59:53 +02:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|