github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/cmd/tendermint/commands/debug/dump.go (about) 1 package debug 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "os" 8 "path/filepath" 9 "time" 10 11 "github.com/spf13/cobra" 12 "github.com/spf13/viper" 13 14 "github.com/ari-anchor/sei-tendermint/config" 15 "github.com/ari-anchor/sei-tendermint/libs/cli" 16 "github.com/ari-anchor/sei-tendermint/libs/log" 17 rpchttp "github.com/ari-anchor/sei-tendermint/rpc/client/http" 18 ) 19 20 func getDumpCmd(logger log.Logger) *cobra.Command { 21 cmd := &cobra.Command{ 22 Use: "dump [output-directory]", 23 Short: "Continuously poll a Tendermint process and dump debugging data into a single location", 24 Long: `Continuously poll a Tendermint process and dump debugging data into a single 25 location at a specified frequency. At each frequency interval, an archived and compressed 26 file will contain node debugging information including the goroutine and heap profiles 27 if enabled.`, 28 Args: cobra.ExactArgs(1), 29 RunE: func(cmd *cobra.Command, args []string) error { 30 outDir := args[0] 31 if outDir == "" { 32 return errors.New("invalid output directory") 33 } 34 frequency, err := cmd.Flags().GetUint(flagFrequency) 35 if err != nil { 36 return fmt.Errorf("flag %q not defined: %w", flagFrequency, err) 37 } 38 39 if frequency == 0 { 40 return errors.New("frequency must be positive") 41 } 42 43 nodeRPCAddr, err := cmd.Flags().GetString(flagNodeRPCAddr) 44 if err != nil { 45 return fmt.Errorf("flag %q not defined: %w", flagNodeRPCAddr, err) 46 } 47 48 profAddr, err := cmd.Flags().GetString(flagProfAddr) 49 if err != nil { 50 return fmt.Errorf("flag %q not defined: %w", flagProfAddr, err) 51 } 52 53 if _, err := os.Stat(outDir); os.IsNotExist(err) { 54 if err := os.Mkdir(outDir, os.ModePerm); err != nil { 55 return fmt.Errorf("failed to create output directory: %w", err) 56 } 57 } 58 59 rpc, err := rpchttp.New(nodeRPCAddr) 60 if err != nil { 61 return fmt.Errorf("failed to create new http client: %w", err) 62 } 63 64 ctx := cmd.Context() 65 66 home := viper.GetString(cli.HomeFlag) 67 conf := config.DefaultConfig() 68 conf = conf.SetRoot(home) 69 config.EnsureRoot(conf.RootDir) 70 71 dumpArgs := dumpDebugDataArgs{ 72 conf: conf, 73 outDir: outDir, 74 profAddr: profAddr, 75 } 76 dumpDebugData(ctx, logger, rpc, dumpArgs) 77 78 ticker := time.NewTicker(time.Duration(frequency) * time.Second) 79 for range ticker.C { 80 dumpDebugData(ctx, logger, rpc, dumpArgs) 81 } 82 83 return nil 84 }, 85 } 86 cmd.Flags().Uint( 87 flagFrequency, 88 30, 89 "the frequency (seconds) in which to poll, aggregate and dump Tendermint debug data", 90 ) 91 92 cmd.Flags().String( 93 flagProfAddr, 94 "", 95 "the profiling server address (<host>:<port>)", 96 ) 97 98 return cmd 99 100 } 101 102 type dumpDebugDataArgs struct { 103 conf *config.Config 104 outDir string 105 profAddr string 106 } 107 108 func dumpDebugData(ctx context.Context, logger log.Logger, rpc *rpchttp.HTTP, args dumpDebugDataArgs) { 109 start := time.Now().UTC() 110 111 tmpDir, err := os.MkdirTemp(args.outDir, "tendermint_debug_tmp") 112 if err != nil { 113 logger.Error("failed to create temporary directory", "dir", tmpDir, "error", err) 114 return 115 } 116 defer os.RemoveAll(tmpDir) 117 118 logger.Info("getting node status...") 119 if err := dumpStatus(ctx, rpc, tmpDir, "status.json"); err != nil { 120 logger.Error("failed to dump node status", "error", err) 121 return 122 } 123 124 logger.Info("getting node network info...") 125 if err := dumpNetInfo(ctx, rpc, tmpDir, "net_info.json"); err != nil { 126 logger.Error("failed to dump node network info", "error", err) 127 return 128 } 129 130 logger.Info("getting node consensus state...") 131 if err := dumpConsensusState(ctx, rpc, tmpDir, "consensus_state.json"); err != nil { 132 logger.Error("failed to dump node consensus state", "error", err) 133 return 134 } 135 136 logger.Info("copying node WAL...") 137 if err := copyWAL(args.conf, tmpDir); err != nil { 138 logger.Error("failed to copy node WAL", "error", err) 139 return 140 } 141 142 if args.profAddr != "" { 143 logger.Info("getting node goroutine profile...") 144 if err := dumpProfile(tmpDir, args.profAddr, "goroutine", 2); err != nil { 145 logger.Error("failed to dump goroutine profile", "error", err) 146 return 147 } 148 149 logger.Info("getting node heap profile...") 150 if err := dumpProfile(tmpDir, args.profAddr, "heap", 2); err != nil { 151 logger.Error("failed to dump heap profile", "error", err) 152 return 153 } 154 } 155 156 outFile := filepath.Join(args.outDir, fmt.Sprintf("%s.zip", start.Format(time.RFC3339))) 157 if err := zipDir(tmpDir, outFile); err != nil { 158 logger.Error("failed to create and compress archive", "file", outFile, "error", err) 159 } 160 }