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. 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. - 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. 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. 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 ## The Triager
Each week, one of the core maintainers will serve as the designated "triager" starting after the 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.", Short: "The Helm package manager for Kubernetes.",
Long: globalUsage, Long: globalUsage,
SilenceUsage: true, 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() flags := cmd.PersistentFlags()
@ -206,6 +226,9 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
// Check for expired repositories // Check for expired repositories
checkForExpiredRepos(settings.RepositoryConfig) checkForExpiredRepos(settings.RepositoryConfig)
// CPU and memory profiling flags that are available to all commands
addProfilingFlags(cmd)
return cmd, nil return cmd, nil
} }

Loading…
Cancel
Save