a9950aedcf
This adds a config file for containerd configuration. It is hard to have structure data on cli flags and the config file should be used for the majority of fields when configuring containerd. There are still a few flags on the daemon that override config file values but flags should take a back seat going forward and should be kept at a minimum. Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
181 lines
3.7 KiB
Go
181 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
type pprofDialer struct {
|
|
proto string
|
|
addr string
|
|
}
|
|
|
|
var pprofCommand = cli.Command{
|
|
Name: "pprof",
|
|
Usage: "provides golang pprof outputs for containerd",
|
|
Flags: []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "debug-socket, d",
|
|
Usage: "socket path for containerd's debug server",
|
|
Value: "/run/containerd/debug.sock",
|
|
},
|
|
},
|
|
Subcommands: []cli.Command{
|
|
pprofGoroutinesCommand,
|
|
pprofHeapCommand,
|
|
pprofProfileCommand,
|
|
pprofTraceCommand,
|
|
pprofBlockCommand,
|
|
pprofThreadcreateCommand,
|
|
},
|
|
}
|
|
|
|
var pprofGoroutinesCommand = cli.Command{
|
|
Name: "goroutines",
|
|
Usage: "dump goroutine stack dump",
|
|
Action: func(context *cli.Context) error {
|
|
client := getPProfClient(context)
|
|
|
|
output, err := httpGetRequest(client, "/debug/pprof/goroutine?debug=2")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer output.Close()
|
|
if _, err := io.Copy(os.Stdout, output); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var pprofHeapCommand = cli.Command{
|
|
Name: "heap",
|
|
Usage: "dump heap profile",
|
|
Action: func(context *cli.Context) error {
|
|
client := getPProfClient(context)
|
|
|
|
output, err := httpGetRequest(client, "/debug/pprof/heap")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer output.Close()
|
|
if _, err := io.Copy(os.Stdout, output); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var pprofProfileCommand = cli.Command{
|
|
Name: "profile",
|
|
Usage: "CPU profile",
|
|
Action: func(context *cli.Context) error {
|
|
client := getPProfClient(context)
|
|
|
|
output, err := httpGetRequest(client, "/debug/pprof/profile")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer output.Close()
|
|
if _, err := io.Copy(os.Stdout, output); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var pprofTraceCommand = cli.Command{
|
|
Name: "trace",
|
|
Usage: "collects execution trace",
|
|
Flags: []cli.Flag{
|
|
cli.DurationFlag{
|
|
Name: "seconds,s",
|
|
Usage: "Trace time (seconds)",
|
|
Value: time.Duration(5 * time.Second),
|
|
},
|
|
},
|
|
Action: func(context *cli.Context) error {
|
|
client := getPProfClient(context)
|
|
|
|
seconds := context.Duration("seconds").Seconds()
|
|
uri := fmt.Sprintf("/debug/pprof/trace?seconds=%v", seconds)
|
|
output, err := httpGetRequest(client, uri)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer output.Close()
|
|
if _, err := io.Copy(os.Stdout, output); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
|
|
},
|
|
}
|
|
|
|
var pprofBlockCommand = cli.Command{
|
|
Name: "block",
|
|
Usage: "goroutine blocking profile",
|
|
Action: func(context *cli.Context) error {
|
|
client := getPProfClient(context)
|
|
|
|
output, err := httpGetRequest(client, "/debug/pprof/block")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer output.Close()
|
|
if _, err := io.Copy(os.Stdout, output); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var pprofThreadcreateCommand = cli.Command{
|
|
Name: "threadcreate",
|
|
Usage: "goroutine blocking profile",
|
|
Action: func(context *cli.Context) error {
|
|
client := getPProfClient(context)
|
|
|
|
output, err := httpGetRequest(client, "/debug/pprof/threadcreate")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer output.Close()
|
|
if _, err := io.Copy(os.Stdout, output); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
func (d *pprofDialer) pprofDial(proto, addr string) (conn net.Conn, err error) {
|
|
return net.Dial(d.proto, d.addr)
|
|
}
|
|
|
|
func getPProfClient(context *cli.Context) *http.Client {
|
|
addr := context.GlobalString("debug-socket")
|
|
dialer := pprofDialer{"unix", addr}
|
|
|
|
tr := &http.Transport{
|
|
Dial: dialer.pprofDial,
|
|
}
|
|
client := &http.Client{Transport: tr}
|
|
return client
|
|
}
|
|
|
|
func httpGetRequest(client *http.Client, request string) (io.ReadCloser, error) {
|
|
resp, err := client.Get("http://." + request)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode != 200 {
|
|
return nil, fmt.Errorf("%s", resp.Status)
|
|
}
|
|
return resp.Body, nil
|
|
}
|