github.com/vipernet-xyz/tm@v0.34.24/cmd/tendermint/commands/debug/dump.go (about)

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