mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-02-14 18:29:53 +00:00
chore: Add CLI flags to enable CPU & memory profiling (#926)
Allow collecting of CPU and memory diagnostics when running troubleshoot CLI applications using --memprofile and --cpuprofile flags. These flags accept file paths if where to store the collected runtime data
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/replicatedhq/troubleshoot/cmd/util"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -26,14 +27,22 @@ func RootCmd() *cobra.Command {
|
||||
if !v.GetBool("debug") {
|
||||
klog.SetLogger(logr.Discard())
|
||||
}
|
||||
logger.SetQuiet(v.GetBool("quiet"))
|
||||
|
||||
if err := util.StartProfiling(); err != nil {
|
||||
logger.Printf("Failed to start profiling: %v", err)
|
||||
}
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
|
||||
logger.SetQuiet(v.GetBool("quiet"))
|
||||
|
||||
return runAnalyzers(v, args[0])
|
||||
},
|
||||
PostRun: func(cmd *cobra.Command, args []string) {
|
||||
if err := util.StopProfiling(); err != nil {
|
||||
logger.Printf("Failed to stop profiling: %v", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
@@ -47,6 +56,9 @@ func RootCmd() *cobra.Command {
|
||||
|
||||
k8sutil.AddFlags(cmd.Flags())
|
||||
|
||||
// CPU and memory profiling flags
|
||||
util.AddProfilingFlags(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/replicatedhq/troubleshoot/cmd/util"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -26,6 +27,10 @@ func RootCmd() *cobra.Command {
|
||||
if !v.GetBool("debug") {
|
||||
klog.SetLogger(logr.Discard())
|
||||
}
|
||||
|
||||
if err := util.StartProfiling(); err != nil {
|
||||
logger.Printf("Failed to start profiling: %v", err)
|
||||
}
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
@@ -33,6 +38,11 @@ func RootCmd() *cobra.Command {
|
||||
logger.SetQuiet(v.GetBool("quiet"))
|
||||
return runCollect(v, args[0])
|
||||
},
|
||||
PostRun: func(cmd *cobra.Command, args []string) {
|
||||
if err := util.StopProfiling(); err != nil {
|
||||
logger.Printf("Failed to stop profiling: %v", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
@@ -58,6 +68,9 @@ func RootCmd() *cobra.Command {
|
||||
|
||||
k8sutil.AddFlags(cmd.Flags())
|
||||
|
||||
// CPU and memory profiling flags
|
||||
util.AddProfilingFlags(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/replicatedhq/troubleshoot/cmd/util"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/preflight"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@@ -22,16 +24,26 @@ that a cluster meets the requirements to run an application.`,
|
||||
SilenceUsage: true,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
v := viper.GetViper()
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
v.BindPFlags(cmd.Flags())
|
||||
|
||||
if !v.GetBool("debug") {
|
||||
klog.SetLogger(logr.Discard())
|
||||
}
|
||||
|
||||
if err := util.StartProfiling(); err != nil {
|
||||
logger.Printf("Failed to start profiling: %v", err)
|
||||
}
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
return preflight.RunPreflights(v.GetBool("interactive"), v.GetString("output"), v.GetString("format"), args)
|
||||
},
|
||||
PostRun: func(cmd *cobra.Command, args []string) {
|
||||
if err := util.StopProfiling(); err != nil {
|
||||
logger.Printf("Failed to stop profiling: %v", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
@@ -39,10 +51,11 @@ that a cluster meets the requirements to run an application.`,
|
||||
cmd.AddCommand(VersionCmd())
|
||||
preflight.AddFlags(cmd.PersistentFlags())
|
||||
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
|
||||
k8sutil.AddFlags(cmd.Flags())
|
||||
|
||||
// CPU and memory profiling flags
|
||||
util.AddProfilingFlags(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,7 @@ func Analyze() *cobra.Command {
|
||||
Short: "analyze a support bundle",
|
||||
Long: `Analyze a support bundle using the Analyzer definitions provided`,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
viper.BindPFlag("bundle", cmd.Flags().Lookup("bundle"))
|
||||
viper.BindPFlag("output", cmd.Flags().Lookup("output"))
|
||||
viper.BindPFlag("quiet", cmd.Flags().Lookup("quiet"))
|
||||
viper.BindPFlags(cmd.Flags())
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
@@ -77,8 +75,6 @@ func Analyze() *cobra.Command {
|
||||
cmd.Flags().MarkHidden("compatibility")
|
||||
cmd.Flags().Bool("quiet", false, "enable/disable error messaging and only show parseable output")
|
||||
|
||||
viper.BindPFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/replicatedhq/troubleshoot/cmd/util"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/logger"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -21,20 +21,32 @@ func RootCmd() *cobra.Command {
|
||||
Long: `A support bundle is an archive of files, output, metrics and state
|
||||
from a server that can be used to assist when troubleshooting a Kubernetes cluster.`,
|
||||
SilenceUsage: true,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
v := viper.GetViper()
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
v.BindPFlags(cmd.Flags())
|
||||
|
||||
if err := util.StartProfiling(); err != nil {
|
||||
logger.Printf("Failed to start profiling: %v", err)
|
||||
}
|
||||
},
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
v := viper.GetViper()
|
||||
if !v.GetBool("debug") {
|
||||
klog.SetLogger(logr.Discard())
|
||||
}
|
||||
logger.SetQuiet(v.GetBool("quiet"))
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
|
||||
logger.SetQuiet(v.GetBool("quiet"))
|
||||
return runTroubleshoot(v, args)
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
if err := util.StopProfiling(); err != nil {
|
||||
logger.Printf("Failed to stop profiling: %v", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
@@ -62,12 +74,11 @@ from a server that can be used to assist when troubleshooting a Kubernetes clust
|
||||
// This flag makes sure we can also disable this and fall back to the default spec.
|
||||
cmd.Flags().Bool("no-uri", false, "When this flag is used, Troubleshoot does not attempt to retrieve the bundle referenced by the uri: field in the spec.`")
|
||||
|
||||
viper.BindPFlags(cmd.Flags())
|
||||
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
|
||||
k8sutil.AddFlags(cmd.Flags())
|
||||
|
||||
// CPU and memory profiling flags
|
||||
util.AddProfilingFlags(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -81,11 +92,3 @@ func initConfig() {
|
||||
viper.SetEnvPrefix("TROUBLESHOOT")
|
||||
viper.AutomaticEnv()
|
||||
}
|
||||
|
||||
func writeFile(filename string, contents []byte) error {
|
||||
if err := ioutil.WriteFile(filename, contents, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
83
cmd/util/profilers.go
Normal file
83
cmd/util/profilers.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
cpuProfileFile *os.File
|
||||
)
|
||||
|
||||
// StartProfiling starts profiling CPU and memory usage if either --cpuprofile or
|
||||
// --memprofile flags were set and bound to viper configurations respectively.
|
||||
func StartProfiling() error {
|
||||
v := viper.GetViper()
|
||||
if v.GetString("cpuprofile") != "" {
|
||||
var err error
|
||||
cpuProfileFile, err = os.Create(v.GetString("cpuprofile"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create CPU profile: %w", err)
|
||||
}
|
||||
if err := pprof.StartCPUProfile(cpuProfileFile); err != nil {
|
||||
cpuProfileFile.Close()
|
||||
cpuProfileFile = nil
|
||||
return fmt.Errorf("could not start CPU profile: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopProfiling stops profiling CPU and memory usage and writes the results to
|
||||
// the files specified by --cpuprofile and --memprofile flags respectively.
|
||||
func StopProfiling() error {
|
||||
v := viper.GetViper()
|
||||
errs := []string{}
|
||||
|
||||
// Stop CPU profiling if it was started
|
||||
if cpuProfileFile != nil {
|
||||
pprof.StopCPUProfile()
|
||||
err := cpuProfileFile.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
}
|
||||
cpuProfileFile = nil
|
||||
}
|
||||
|
||||
if v.GetString("memprofile") != "" {
|
||||
f, err := os.Create(v.GetString("memprofile"))
|
||||
if err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
goto BREAK_FROM_MEMPROFILE_IF
|
||||
}
|
||||
runtime.GC() // get up-to-date statistics
|
||||
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
goto BREAK_FROM_MEMPROFILE_IF
|
||||
}
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
BREAK_FROM_MEMPROFILE_IF:
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("errors while stopping profiling: [%s]", strings.Join(errs, ", "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddProfilingFlags adds the --cpuprofile and --memprofile flags to the given command.
|
||||
func AddProfilingFlags(cmd *cobra.Command) {
|
||||
// Persistent flags to make available to subcommands
|
||||
cmd.PersistentFlags().String("cpuprofile", "", "File path to write cpu profiling data")
|
||||
cmd.PersistentFlags().String("memprofile", "", "File path to write memory profiling data")
|
||||
}
|
||||
Reference in New Issue
Block a user