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 }