github.com/containerd/Containerd@v1.4.13/cmd/ctr/commands/pprof/pprof.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package pprof
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"net/http"
    23  	"os"
    24  	"time"
    25  
    26  	"github.com/containerd/containerd/defaults"
    27  	"github.com/pkg/errors"
    28  	"github.com/urfave/cli"
    29  )
    30  
    31  type pprofDialer struct {
    32  	proto string
    33  	addr  string
    34  }
    35  
    36  // Command is the cli command for providing golang pprof outputs for containerd
    37  var Command = cli.Command{
    38  	Name:  "pprof",
    39  	Usage: "provide golang pprof outputs for containerd",
    40  	Flags: []cli.Flag{
    41  		cli.StringFlag{
    42  			Name:  "debug-socket, d",
    43  			Usage: "socket path for containerd's debug server",
    44  			Value: defaults.DefaultDebugAddress,
    45  		},
    46  	},
    47  	Subcommands: []cli.Command{
    48  		pprofBlockCommand,
    49  		pprofGoroutinesCommand,
    50  		pprofHeapCommand,
    51  		pprofProfileCommand,
    52  		pprofThreadcreateCommand,
    53  		pprofTraceCommand,
    54  	},
    55  }
    56  
    57  var pprofGoroutinesCommand = cli.Command{
    58  	Name:  "goroutines",
    59  	Usage: "dump goroutine stack dump",
    60  	Action: func(context *cli.Context) error {
    61  		client := getPProfClient(context)
    62  
    63  		output, err := httpGetRequest(client, "/debug/pprof/goroutine?debug=2")
    64  		if err != nil {
    65  			return err
    66  		}
    67  		defer output.Close()
    68  		_, err = io.Copy(os.Stdout, output)
    69  		return err
    70  	},
    71  }
    72  
    73  var pprofHeapCommand = cli.Command{
    74  	Name:  "heap",
    75  	Usage: "dump heap profile",
    76  	Action: func(context *cli.Context) error {
    77  		client := getPProfClient(context)
    78  
    79  		output, err := httpGetRequest(client, "/debug/pprof/heap")
    80  		if err != nil {
    81  			return err
    82  		}
    83  		defer output.Close()
    84  		_, err = io.Copy(os.Stdout, output)
    85  		return err
    86  	},
    87  }
    88  
    89  var pprofProfileCommand = cli.Command{
    90  	Name:  "profile",
    91  	Usage: "CPU profile",
    92  	Flags: []cli.Flag{
    93  		cli.DurationFlag{
    94  			Name:  "seconds,s",
    95  			Usage: "duration for collection (seconds)",
    96  			Value: 30 * time.Second,
    97  		},
    98  	},
    99  	Action: func(context *cli.Context) error {
   100  		client := getPProfClient(context)
   101  
   102  		seconds := context.Duration("seconds").Seconds()
   103  		output, err := httpGetRequest(client, fmt.Sprintf("/debug/pprof/profile?seconds=%v", seconds))
   104  		if err != nil {
   105  			return err
   106  		}
   107  		defer output.Close()
   108  		_, err = io.Copy(os.Stdout, output)
   109  		return err
   110  	},
   111  }
   112  
   113  var pprofTraceCommand = cli.Command{
   114  	Name:  "trace",
   115  	Usage: "collect execution trace",
   116  	Flags: []cli.Flag{
   117  		cli.DurationFlag{
   118  			Name:  "seconds,s",
   119  			Usage: "trace time (seconds)",
   120  			Value: 5 * time.Second,
   121  		},
   122  	},
   123  	Action: func(context *cli.Context) error {
   124  		client := getPProfClient(context)
   125  
   126  		seconds := context.Duration("seconds").Seconds()
   127  		uri := fmt.Sprintf("/debug/pprof/trace?seconds=%v", seconds)
   128  		output, err := httpGetRequest(client, uri)
   129  		if err != nil {
   130  			return err
   131  		}
   132  		defer output.Close()
   133  		_, err = io.Copy(os.Stdout, output)
   134  		return err
   135  	},
   136  }
   137  
   138  var pprofBlockCommand = cli.Command{
   139  	Name:  "block",
   140  	Usage: "goroutine blocking profile",
   141  	Action: func(context *cli.Context) error {
   142  		client := getPProfClient(context)
   143  
   144  		output, err := httpGetRequest(client, "/debug/pprof/block")
   145  		if err != nil {
   146  			return err
   147  		}
   148  		defer output.Close()
   149  		_, err = io.Copy(os.Stdout, output)
   150  		return err
   151  	},
   152  }
   153  
   154  var pprofThreadcreateCommand = cli.Command{
   155  	Name:  "threadcreate",
   156  	Usage: "goroutine thread creating profile",
   157  	Action: func(context *cli.Context) error {
   158  		client := getPProfClient(context)
   159  
   160  		output, err := httpGetRequest(client, "/debug/pprof/threadcreate")
   161  		if err != nil {
   162  			return err
   163  		}
   164  		defer output.Close()
   165  		_, err = io.Copy(os.Stdout, output)
   166  		return err
   167  	},
   168  }
   169  
   170  func getPProfClient(context *cli.Context) *http.Client {
   171  	dialer := getPProfDialer(context.GlobalString("debug-socket"))
   172  
   173  	tr := &http.Transport{
   174  		Dial: dialer.pprofDial,
   175  	}
   176  	client := &http.Client{Transport: tr}
   177  	return client
   178  }
   179  
   180  func httpGetRequest(client *http.Client, request string) (io.ReadCloser, error) {
   181  	resp, err := client.Get("http://." + request)
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	if resp.StatusCode != 200 {
   186  		return nil, errors.Errorf("http get failed with status: %s", resp.Status)
   187  	}
   188  	return resp.Body, nil
   189  }