Skip to content

Commit 49690b7

Browse files
Merge pull request #929 from njhale/pprof-option
Add optional profiling
2 parents 2bd381f + 358120e commit 49690b7

File tree

4 files changed

+132
-3
lines changed

4 files changed

+132
-3
lines changed

Documentation/design/profiling.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Profiling OLM Operators
2+
3+
OLM's `olm` and `catalog` commands support serving profiling samples via the `--profiling` option.
4+
5+
```sh
6+
# run olm operator with profiling enabled
7+
$ go run cmd/olm/main.go --profiling --kubeconfig ~/.kube/config
8+
```
9+
10+
Samples are in a format recognized by [pprof](https://golang.org/pkg/net/http/pprof) and an index of available profile types is made available at `https://127.0.0.1:8080/debug/pprof`.
11+
12+
If profiling is enabled, but operators are running on a kubernetes cluster, a convienient way to expose samples locally is with `kubectl port-forward`:
13+
14+
```sh
15+
# forward traffic from 127.0.0.1:8080 to port 8080 of catalog operator pods
16+
$ kubectl -n <olm-namespace> port-forward deployments/catalog-operator
17+
```
18+
19+
When profiling is enabled, `go tool pprof` can be used to export and visualize samples:
20+
21+
```sh
22+
# assuming a catalog operator's samples are accessible at 127.0.0.1:8080:
23+
# show in-use heap memory in top format
24+
$ go tool pprof -top http://127.0.0.1:8080/debug/pprof/heap
25+
Fetching profile over HTTP from http://127.0.0.1:8080/debug/pprof/heap
26+
Saved profile in /Users/nhale/pprof/pprof.catalog.alloc_objects.alloc_space.inuse_objects.inuse_space.013.pb.gz
27+
File: catalog
28+
Type: inuse_space
29+
Time: Jun 27, 2019 at 12:27pm (EDT)
30+
Showing nodes accounting for 2202.74kB, 100% of 2202.74kB total
31+
flat flat% sum% cum cum%
32+
650.62kB 29.54% 29.54% 650.62kB 29.54% bufio.NewWriterSize
33+
520.04kB 23.61% 53.15% 520.04kB 23.61% golang.org/x/net/http2.NewFramer.func1
34+
520.04kB 23.61% 76.75% 520.04kB 23.61% sync.(*Map).LoadOrStore
35+
512.03kB 23.25% 100% 512.03kB 23.25% github.com/modern-go/reflect2.newUnsafeStructField
36+
...
37+
38+
# save in-use objects graph to svg file
39+
$ go tool pprof -sample_index=inuse_objects -svg http://127.0.0.1:8080/debug/pprof/heap
40+
Fetching profile over HTTP from http://127.0.0.1:8080/debug/pprof/heap
41+
Saved profile in /Users/<user>/pprof/pprof.catalog.alloc_objects.alloc_space.inuse_objects.inuse_space.01.pb.gz
42+
Generating report in profile001.svg
43+
```

cmd/catalog/main.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@ import (
1010
"time"
1111

1212
configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
13-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
14-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorstatus"
1513
"github.com/prometheus/client_golang/prometheus/promhttp"
1614
log "github.com/sirupsen/logrus"
1715
v1 "k8s.io/api/core/v1"
1816
utilclock "k8s.io/apimachinery/pkg/util/clock"
1917
"k8s.io/client-go/tools/clientcmd"
2018

2119
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog"
20+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
21+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorstatus"
22+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile"
2223
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/signals"
2324
"github.com/operator-framework/operator-lifecycle-manager/pkg/metrics"
2425
olmversion "github.com/operator-framework/operator-lifecycle-manager/pkg/version"
@@ -61,6 +62,9 @@ var (
6162

6263
tlsCertPath = flag.String(
6364
"tls-cert", "", "Path to use for certificate key (requires tls-key)")
65+
66+
profiling = flag.Bool(
67+
"profiling", false, "serve profiling data (on port 8080)")
6468
)
6569

6670
func init() {
@@ -114,6 +118,13 @@ func main() {
114118
healthMux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
115119
w.WriteHeader(http.StatusOK)
116120
})
121+
122+
// Serve profiling if enabled
123+
if *profiling {
124+
logger.Infof("profiling enabled")
125+
profile.RegisterHandlers(healthMux)
126+
}
127+
117128
go http.ListenAndServe(":8080", healthMux)
118129

119130
metricsMux := http.NewServeMux()

cmd/olm/main.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"time"
1111

1212
configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
13-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorstatus"
1413
"github.com/prometheus/client_golang/prometheus/promhttp"
1514
log "github.com/sirupsen/logrus"
1615
v1 "k8s.io/api/core/v1"
@@ -19,6 +18,8 @@ import (
1918
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client"
2019
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm"
2120
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
21+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorstatus"
22+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile"
2223
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/signals"
2324
"github.com/operator-framework/operator-lifecycle-manager/pkg/metrics"
2425
olmversion "github.com/operator-framework/operator-lifecycle-manager/pkg/version"
@@ -59,6 +60,9 @@ var (
5960

6061
tlsCertPath = flag.String(
6162
"tls-cert", "", "Path to use for certificate key (requires tls-key)")
63+
64+
profiling = flag.Bool(
65+
"profiling", false, "serve profiling data (on port 8080)")
6266
)
6367

6468
func init() {
@@ -114,6 +118,13 @@ func main() {
114118
healthMux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
115119
w.WriteHeader(http.StatusOK)
116120
})
121+
122+
// Serve profiling if enabled
123+
if *profiling {
124+
logger.Infof("profiling enabled")
125+
profile.RegisterHandlers(healthMux)
126+
}
127+
117128
go func() {
118129
err := http.ListenAndServe(":8080", healthMux)
119130
if err != nil {

pkg/lib/profile/profile.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package profile
2+
3+
import (
4+
"net/http"
5+
"net/http/pprof"
6+
)
7+
8+
type profileConfig struct {
9+
pprof bool
10+
cmdline bool
11+
profile bool
12+
symbol bool
13+
trace bool
14+
}
15+
16+
// Option applies a configuration option to the given config.
17+
type Option func(p *profileConfig)
18+
19+
func (p *profileConfig) apply(options []Option) {
20+
if len(options) == 0 {
21+
// If no options are given, default to all
22+
p.pprof = true
23+
p.cmdline = true
24+
p.profile = true
25+
p.symbol = true
26+
p.trace = true
27+
28+
return
29+
}
30+
31+
for _, o := range options {
32+
o(p)
33+
}
34+
}
35+
36+
func defaultProfileConfig() *profileConfig {
37+
// Initialize config
38+
return &profileConfig{}
39+
}
40+
41+
// RegisterHandlers registers profile Handlers with the given ServeMux.
42+
//
43+
// The Handlers registered are determined by the given options.
44+
// If no options are given, all available handlers are registered by default.
45+
func RegisterHandlers(mux *http.ServeMux, options ...Option) {
46+
config := defaultProfileConfig()
47+
config.apply(options)
48+
49+
if config.pprof {
50+
mux.HandleFunc("/debug/pprof/", pprof.Index)
51+
}
52+
if config.cmdline {
53+
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
54+
}
55+
if config.profile {
56+
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
57+
}
58+
if config.symbol {
59+
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
60+
}
61+
if config.trace {
62+
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
63+
}
64+
}

0 commit comments

Comments
 (0)