mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-08 22:23:11 +00:00
## Issue
Implement a new subcommand: tendermint debug. This subcommand itself has two subcommands:
$ tendermint debug kill <pid> </path/to/out.zip> --home=</path/to/app.d>
Writes debug info into a compressed archive. The archive will contain the following:
├── config.toml
├── consensus_state.json
├── net_info.json
├── stacktrace.out
├── status.json
└── wal
The Tendermint process will be killed.
$ tendermint debug dump </path/to/out> --home=</path/to/app.d>
This command will perform similar to kill except it only polls the node and dumps debugging data every frequency seconds to a compressed archive under a given destination directory. Each archive will contain:
├── consensus_state.json
├── goroutine.out
├── heap.out
├── net_info.json
├── status.json
└── wal
Note:
goroutine.out and heap.out will only be written if a profile address is provided and is operational.
This command is blocking and will log any error.
replaces: #3327
closes: #3249
## Commits:
* Implement debug tool command stubs
* Implement net getters and zip logic
* Update zip dir API and add home flag
* Add simple godocs for kill aux functions
* Move IO util to new file and implement copy WAL func
* Implement copy config function
* Implement killProc
* Remove debug fmt
* Validate output file input
* Direct STDERR to file
* Godoc updates
* Sleep prior to killing tail proc
* Minor cleanup of godocs
* Move debug command and add make target
* Rename command handler function
* Add example to command long descr
* Move current implementation to cmd/tendermint/commands/debug
* Update kill cmd long description
* Implement dump command
* Add pending log entry
* Add gosec nolint
* Add error check for Mkdir
* Add os.IsNotExist(err)
* Add to debugging section in running-in-prod doc
83 lines
2.4 KiB
Go
83 lines
2.4 KiB
Go
package debug
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
|
|
"github.com/pkg/errors"
|
|
cfg "github.com/tendermint/tendermint/config"
|
|
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
|
)
|
|
|
|
// dumpStatus gets node status state dump from the Tendermint RPC and writes it
|
|
// to file. It returns an error upon failure.
|
|
func dumpStatus(rpc *rpcclient.HTTP, dir, filename string) error {
|
|
status, err := rpc.Status()
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to get node status")
|
|
}
|
|
|
|
return writeStateJSONToFile(status, dir, filename)
|
|
}
|
|
|
|
// dumpNetInfo gets network information state dump from the Tendermint RPC and
|
|
// writes it to file. It returns an error upon failure.
|
|
func dumpNetInfo(rpc *rpcclient.HTTP, dir, filename string) error {
|
|
netInfo, err := rpc.NetInfo()
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to get node network information")
|
|
}
|
|
|
|
return writeStateJSONToFile(netInfo, dir, filename)
|
|
}
|
|
|
|
// dumpConsensusState gets consensus state dump from the Tendermint RPC and
|
|
// writes it to file. It returns an error upon failure.
|
|
func dumpConsensusState(rpc *rpcclient.HTTP, dir, filename string) error {
|
|
consDump, err := rpc.DumpConsensusState()
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to get node consensus dump")
|
|
}
|
|
|
|
return writeStateJSONToFile(consDump, dir, filename)
|
|
}
|
|
|
|
// copyWAL copies the Tendermint node's WAL file. It returns an error if the
|
|
// WAL file cannot be read or copied.
|
|
func copyWAL(conf *cfg.Config, dir string) error {
|
|
walPath := conf.Consensus.WalFile()
|
|
walFile := filepath.Base(walPath)
|
|
|
|
return copyFile(walPath, filepath.Join(dir, walFile))
|
|
}
|
|
|
|
// copyConfig copies the Tendermint node's config file. It returns an error if
|
|
// the config file cannot be read or copied.
|
|
func copyConfig(home, dir string) error {
|
|
configFile := "config.toml"
|
|
configPath := filepath.Join(home, "config", configFile)
|
|
|
|
return copyFile(configPath, filepath.Join(dir, configFile))
|
|
}
|
|
|
|
func dumpProfile(dir, addr, profile string, debug int) error {
|
|
endpoint := fmt.Sprintf("%s/debug/pprof/%s?debug=%d", addr, profile, debug)
|
|
|
|
resp, err := http.Get(endpoint) // nolint: gosec
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to query for %s profile", profile)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to read %s profile response body", profile)
|
|
}
|
|
|
|
return ioutil.WriteFile(path.Join(dir, fmt.Sprintf("%s.out", profile)), body, os.ModePerm)
|
|
}
|