github.com/number571/tendermint@v0.34.11-gost/cmd/tendermint/commands/debug/kill.go (about) 1 package debug 2 3 import ( 4 "errors" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strconv" 11 "syscall" 12 "time" 13 14 "github.com/spf13/cobra" 15 "github.com/spf13/viper" 16 17 cfg "github.com/number571/tendermint/config" 18 "github.com/number571/tendermint/libs/cli" 19 rpchttp "github.com/number571/tendermint/rpc/client/http" 20 ) 21 22 var killCmd = &cobra.Command{ 23 Use: "kill [pid] [compressed-output-file]", 24 Short: "Kill a Tendermint process while aggregating and packaging debugging data", 25 Long: `Kill a Tendermint process while also aggregating Tendermint process data 26 such as the latest node state, including consensus and networking state, 27 go-routine state, and the node's WAL and config information. This aggregated data 28 is packaged into a compressed archive. 29 30 Example: 31 $ tendermint debug kill 34255 /path/to/tm-debug.zip`, 32 Args: cobra.ExactArgs(2), 33 RunE: killCmdHandler, 34 } 35 36 func killCmdHandler(cmd *cobra.Command, args []string) error { 37 pid, err := strconv.ParseUint(args[0], 10, 64) 38 if err != nil { 39 return err 40 } 41 42 outFile := args[1] 43 if outFile == "" { 44 return errors.New("invalid output file") 45 } 46 47 rpc, err := rpchttp.New(nodeRPCAddr) 48 if err != nil { 49 return fmt.Errorf("failed to create new http client: %w", err) 50 } 51 52 home := viper.GetString(cli.HomeFlag) 53 conf := cfg.DefaultConfig() 54 conf = conf.SetRoot(home) 55 cfg.EnsureRoot(conf.RootDir) 56 57 // Create a temporary directory which will contain all the state dumps and 58 // relevant files and directories that will be compressed into a file. 59 tmpDir, err := ioutil.TempDir(os.TempDir(), "tendermint_debug_tmp") 60 if err != nil { 61 return fmt.Errorf("failed to create temporary directory: %w", err) 62 } 63 defer os.RemoveAll(tmpDir) 64 65 logger.Info("getting node status...") 66 if err := dumpStatus(rpc, tmpDir, "status.json"); err != nil { 67 return err 68 } 69 70 logger.Info("getting node network info...") 71 if err := dumpNetInfo(rpc, tmpDir, "net_info.json"); err != nil { 72 return err 73 } 74 75 logger.Info("getting node consensus state...") 76 if err := dumpConsensusState(rpc, tmpDir, "consensus_state.json"); err != nil { 77 return err 78 } 79 80 logger.Info("copying node WAL...") 81 if err := copyWAL(conf, tmpDir); err != nil { 82 if !os.IsNotExist(err) { 83 return err 84 } 85 86 logger.Info("node WAL does not exist; continuing...") 87 } 88 89 logger.Info("copying node configuration...") 90 if err := copyConfig(home, tmpDir); err != nil { 91 return err 92 } 93 94 logger.Info("killing Tendermint process") 95 if err := killProc(pid, tmpDir); err != nil { 96 return err 97 } 98 99 logger.Info("archiving and compressing debug directory...") 100 return zipDir(tmpDir, outFile) 101 } 102 103 // killProc attempts to kill the Tendermint process with a given PID with an 104 // ABORT signal which should result in a goroutine stacktrace. The PID's STDERR 105 // is tailed and piped to a file under the directory dir. An error is returned 106 // if the output file cannot be created or the tail command cannot be started. 107 // An error is not returned if any subsequent syscall fails. 108 func killProc(pid uint64, dir string) error { 109 // pipe STDERR output from tailing the Tendermint process to a file 110 // 111 // NOTE: This will only work on UNIX systems. 112 cmd := exec.Command("tail", "-f", fmt.Sprintf("/proc/%d/fd/2", pid)) // nolint: gosec 113 114 outFile, err := os.Create(filepath.Join(dir, "stacktrace.out")) 115 if err != nil { 116 return err 117 } 118 defer outFile.Close() 119 120 cmd.Stdout = outFile 121 cmd.Stderr = outFile 122 123 if err := cmd.Start(); err != nil { 124 return err 125 } 126 127 // kill the underlying Tendermint process and subsequent tailing process 128 go func() { 129 // Killing the Tendermint process with the '-ABRT|-6' signal will result in 130 // a goroutine stacktrace. 131 p, err := os.FindProcess(int(pid)) 132 if err != nil { 133 fmt.Fprintf(os.Stderr, "failed to find PID to kill Tendermint process: %s", err) 134 } else if err = p.Signal(syscall.SIGABRT); err != nil { 135 fmt.Fprintf(os.Stderr, "failed to kill Tendermint process: %s", err) 136 } 137 138 // allow some time to allow the Tendermint process to be killed 139 // 140 // TODO: We should 'wait' for a kill to succeed (e.g. poll for PID until it 141 // cannot be found). Regardless, this should be ample time. 142 time.Sleep(5 * time.Second) 143 144 if err := cmd.Process.Kill(); err != nil { 145 fmt.Fprintf(os.Stderr, "failed to kill Tendermint process output redirection: %s", err) 146 } 147 }() 148 149 if err := cmd.Wait(); err != nil { 150 // only return an error not invoked by a manual kill 151 if _, ok := err.(*exec.ExitError); !ok { 152 return err 153 } 154 } 155 156 return nil 157 }