Merge pull request #13481 from banjoh/em/add-cpu-memory-profiling-flags

feat: Enable CPU and memory profiling
pull/11626/merge
Scott Rigby 7 months ago committed by GitHub
commit dea2069374
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -276,12 +276,26 @@ 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.
### Profiling PRs
If your contribution requires profiling to check memory and/or CPU usage, you can set `HELM_PPROF_CPU_PROFILE=/path/to/cpu.prof` and/or `HELM_PPROF_MEM_PROFILE=/path/to/mem.prof` environment variables to 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 results.
Example analysing collected profiling data
```
HELM_PPROF_CPU_PROFILE=cpu.prof HELM_PPROF_MEM_PROFILE=mem.prof helm show all bitnami/nginx
# Visualize graphs. You need to have installed graphviz package in your system
go tool pprof -http=":8000" cpu.prof
go tool pprof -http=":8001" mem.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,91 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"errors"
"fmt"
"os"
"runtime"
"runtime/pprof"
)
var (
cpuProfileFile *os.File
cpuProfilePath string
memProfilePath string
)
func init() {
cpuProfilePath = os.Getenv("HELM_PPROF_CPU_PROFILE")
memProfilePath = os.Getenv("HELM_PPROF_MEM_PROFILE")
}
// startProfiling starts profiling CPU usage if HELM_PPROF_CPU_PROFILE is set
// to a file path. It returns an error if the file could not be created or
// CPU profiling could not be started.
func startProfiling() error {
if cpuProfilePath != "" {
var err error
cpuProfileFile, err = os.Create(cpuProfilePath)
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.
// It writes memory profile to the file path specified in HELM_PPROF_MEM_PROFILE
// environment variable.
func stopProfiling() error {
errs := []error{}
// Stop CPU profiling if it was started
if cpuProfileFile != nil {
pprof.StopCPUProfile()
err := cpuProfileFile.Close()
if err != nil {
errs = append(errs, err)
}
cpuProfileFile = nil
}
if memProfilePath != "" {
f, err := os.Create(memProfilePath)
if err != nil {
errs = append(errs, err)
}
defer f.Close()
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
errs = append(errs, err)
}
}
if err := errors.Join(errs...); err != nil {
return fmt.Errorf("error(s) while stopping profiling: %w", err)
}
return nil
}

@ -95,6 +95,16 @@ 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(_ *cobra.Command, _ []string) {
if err := startProfiling(); err != nil {
log.Printf("Warning: Failed to start profiling: %v", err)
}
},
PersistentPostRun: func(_ *cobra.Command, _ []string) {
if err := stopProfiling(); err != nil {
log.Printf("Warning: Failed to stop profiling: %v", err)
}
},
} }
flags := cmd.PersistentFlags() flags := cmd.PersistentFlags()

Loading…
Cancel
Save