github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/internal/cli/debug_pprof.go (about) 1 package cli 2 3 // Based on https://github.com/hashicorp/nomad/blob/main/command/operator_debug.go 4 5 import ( 6 "context" 7 "fmt" 8 "strings" 9 10 "google.golang.org/grpc" 11 12 "github.com/ethereum/go-ethereum/internal/cli/flagset" 13 "github.com/ethereum/go-ethereum/internal/cli/server/proto" 14 ) 15 16 type DebugPprofCommand struct { 17 *Meta2 18 19 seconds uint64 20 output string 21 skiptrace bool 22 } 23 24 func (p *DebugPprofCommand) MarkDown() string { 25 items := []string{ 26 "# Debug Pprof", 27 "The ```debug pprof <enode>``` command will create an archive containing bor pprof traces.", 28 p.Flags().MarkDown(), 29 } 30 31 return strings.Join(items, "\n\n") 32 } 33 34 // Help implements the cli.Command interface 35 func (d *DebugPprofCommand) Help() string { 36 return `Usage: bor debug 37 38 Build an archive containing Bor pprof traces 39 40 ` + d.Flags().Help() 41 } 42 43 func (d *DebugPprofCommand) Flags() *flagset.Flagset { 44 flags := d.NewFlagSet("debug") 45 46 flags.Uint64Flag(&flagset.Uint64Flag{ 47 Name: "seconds", 48 Usage: "seconds to profile", 49 Value: &d.seconds, 50 Default: 2, 51 }) 52 flags.StringFlag(&flagset.StringFlag{ 53 Name: "output", 54 Value: &d.output, 55 Usage: "Output directory", 56 }) 57 58 // Trace profiles can be expensive and take too much size (for grpc). 59 // This flag will help in making it optional. 60 flags.BoolFlag(&flagset.BoolFlag{ 61 Name: "skiptrace", 62 Value: &d.skiptrace, 63 Usage: "Skip running the trace", 64 Default: false, 65 }) 66 67 return flags 68 } 69 70 // Synopsis implements the cli.Command interface 71 func (d *DebugPprofCommand) Synopsis() string { 72 return "Build an archive containing Bor pprof traces" 73 } 74 75 // Run implements the cli.Command interface 76 func (d *DebugPprofCommand) Run(args []string) int { 77 flags := d.Flags() 78 if err := flags.Parse(args); err != nil { 79 d.UI.Error(err.Error()) 80 return 1 81 } 82 83 clt, err := d.BorConn() 84 if err != nil { 85 d.UI.Error(err.Error()) 86 return 1 87 } 88 89 dEnv := &debugEnv{ 90 output: d.output, 91 prefix: "bor-debug-", 92 } 93 if err := dEnv.init(); err != nil { 94 d.UI.Error(err.Error()) 95 return 1 96 } 97 98 d.UI.Output("Starting debugger...") 99 d.UI.Output("") 100 101 pprofProfile := func(ctx context.Context, profile string, filename string) error { 102 req := &proto.DebugPprofRequest{ 103 Seconds: int64(d.seconds), 104 } 105 106 switch profile { 107 case "cpu": 108 req.Type = proto.DebugPprofRequest_CPU 109 case "trace": 110 req.Type = proto.DebugPprofRequest_TRACE 111 default: 112 req.Type = proto.DebugPprofRequest_LOOKUP 113 req.Profile = profile 114 } 115 116 stream, err := clt.DebugPprof(ctx, req, grpc.MaxCallRecvMsgSize(1024*1024*1024)) 117 118 if err != nil { 119 return err 120 } 121 122 if err := dEnv.writeFromStream(filename+".prof", stream); err != nil { 123 return err 124 } 125 126 return nil 127 } 128 129 ctx, cancelFn := context.WithCancel(context.Background()) 130 trapSignal(cancelFn) 131 132 // Only take cpu and heap profiles by default 133 profiles := map[string]string{ 134 "heap": "heap", 135 "cpu": "cpu", 136 "mutex": "mutex", 137 } 138 139 if !d.skiptrace { 140 profiles["trace"] = "trace" 141 } 142 143 for profile, filename := range profiles { 144 if err := pprofProfile(ctx, profile, filename); err != nil { 145 d.UI.Error(fmt.Sprintf("Error creating profile '%s': %v", profile, err)) 146 return 1 147 } 148 } 149 150 // append the status 151 { 152 statusResp, err := clt.Status(ctx, &proto.StatusRequest{}) 153 if err != nil { 154 d.UI.Output(fmt.Sprintf("Failed to get status: %v", err)) 155 return 1 156 } 157 if err := dEnv.writeJSON("status.json", statusResp); err != nil { 158 d.UI.Error(err.Error()) 159 return 1 160 } 161 } 162 163 if err := dEnv.finish(); err != nil { 164 d.UI.Error(err.Error()) 165 return 1 166 } 167 168 if d.output != "" { 169 d.UI.Output(fmt.Sprintf("Created debug directory: %s", dEnv.dst)) 170 } else { 171 d.UI.Output(fmt.Sprintf("Created debug archive: %s", dEnv.tarName())) 172 } 173 174 return 0 175 }