feat: Add flags to enable CPU and memory profiling

Add capability to profile cli command using
https://go.dev/blog/pprof

Signed-off-by: Evans Mungai <mbuevans@gmail.com>
pull/13481/head
Evans Mungai 10 months ago
parent a3c903e4c6
commit baf8bffc12
No known key found for this signature in database
GPG Key ID: BBEB812143DD14E1

@ -276,12 +276,24 @@ Like any good open source project, we use Pull Requests (PRs) to track code chan
or explicitly request another OWNER do that for them.
- If the owner of a PR is _not_ listed in `OWNERS`, any core maintainer may merge the PR.
#### Documentation PRs
### Documentation PRs
Documentation PRs should be made on the docs repo: <https://github.com/helm/helm-www>. Keeping Helm's documentation up to date is highly desirable, and is recommended for all user facing changes. Accurate and helpful documentation is critical for effectively communicating Helm's behavior to a wide audience.
Small, ad-hoc changes/PRs to Helm which introduce user facing changes, which would benefit from documentation changes, should apply the `docs needed` label. Larger changes associated with a HIP should track docs via that HIP. The `docs needed` label doesn't block PRs, and maintainers/PR reviewers should apply discretion judging in whether the `docs needed` label should be applied.
### Testing PRs
During development, you need to add automated tests where possible. There are a few `make test*` targets that you can use to execute your unit or integration tests. If your contribution requires profiling to check memory and/or CPU usage, you can use `--cpuprofile` and/or `--memprofile` global cli flags to run collect runtime profiling data for analysis. You can use Golang's [pprof](https://github.com/google/pprof/blob/main/doc/README.md) tool to inspect the profiling data.
Example invocation that collects profiling data
```
helm show all bitnami/nginx --memprofile mem.prof --cpuprofile cpu.prof
# Visualize graphs. You need to have installed graphviz in you system
go tool pprof -http=":8000" cpu.prof
```
## The Triager
Each week, one of the core maintainers will serve as the designated "triager" starting after the

@ -0,0 +1,76 @@
// Profile CPU and memory usage of Helm commands
package main
import (
"fmt"
"os"
"runtime"
"runtime/pprof"
"strings"
"github.com/spf13/cobra"
)
var (
cpuProfileFile *os.File
)
// startProfiling starts profiling CPU usage
func startProfiling(cpuprofile string) error {
if cpuprofile != "" {
var err error
cpuProfileFile, err = os.Create(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(memprofile string) error {
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 memprofile != "" {
f, err := os.Create(memprofile)
if err != nil {
errs = append(errs, err.Error())
}
defer f.Close()
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
errs = append(errs, err.Error())
}
}
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")
}

@ -95,6 +95,26 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
Short: "The Helm package manager for Kubernetes.",
Long: globalUsage,
SilenceUsage: true,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cpuprof, err := cmd.Flags().GetString("cpuprofile")
if err != nil {
log.Printf("Warning: Failed to get cpuprofile flag: %v", err)
}
if err := startProfiling(cpuprof); err != nil {
log.Printf("Warning: Failed to start profiling: %v", err)
}
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
memprof, err := cmd.Flags().GetString("memprofile")
if err != nil {
log.Printf("Warning: Failed to get memprofile flag: %v", err)
}
if err := stopProfiling(memprof); err != nil {
log.Printf("Warning: Failed to stop profiling: %v", err)
}
},
}
flags := cmd.PersistentFlags()
@ -206,6 +226,9 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
// Check for expired repositories
checkForExpiredRepos(settings.RepositoryConfig)
// CPU and memory profiling flags that are available to all commands
addProfilingFlags(cmd)
return cmd, nil
}

Loading…
Cancel
Save