Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

metrics: add pprof support #138

Merged
merged 1 commit into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"bufio"
"fmt"
"net/http"
"net/http/pprof"
"sort"
"strings"
"time"
Expand Down Expand Up @@ -99,6 +100,12 @@ type CSIMetricsManager interface {
// RegisterToServer registers an HTTP handler for this metrics manager to the
// given server at the specified address/path.
RegisterToServer(s Server, metricsPath string)

// RegisterPprofToServer registers the HTTP handlers necessary to enable pprof
// for this metrics manager to the given server at the usual path.
// This function is not needed when using DefaultServeMux as the Server since
// the handlers will automatically be registered when importing pprof.
RegisterPprofToServer(s Server)
}

// Server represents any type that could serve HTTP requests for the metrics
Expand Down Expand Up @@ -388,6 +395,20 @@ func (cmm *csiMetricsManager) RegisterToServer(s Server, metricsPath string) {
ErrorHandling: metrics.ContinueOnError}))
}

// RegisterPprofToServer registers the HTTP handlers necessary to enable pprof
// for this metrics manager to the given server at the usual path.
// This function is not needed when using DefaultServeMux as the Server since
// the handlers will automatically be registered when importing pprof.
func (cmm *csiMetricsManager) RegisterPprofToServer(s Server) {
// Needed handlers can be seen here:
// https://github.com/golang/go/blob/master/src/net/http/pprof/pprof.go#L27
s.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
s.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
s.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
s.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
s.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
}

// VerifyMetricsMatch is a helper function that verifies that the expected and
// actual metrics are identical excluding metricToIgnore.
// This method is only used by tests. Ideally it should be in the _test file,
Expand Down
59 changes: 59 additions & 0 deletions metrics/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,65 @@ func TestRegisterToServer_Noop(t *testing.T) {
}
}

func TestRegisterPprofToServer_AllEndpointsAvailable(t *testing.T) {
endpoints := []string{
"/debug/pprof/",
"/debug/pprof/cmdline",
"/debug/pprof/profile",
"/debug/pprof/symbol",
"/debug/pprof/trace",
}

for _, endpoint := range endpoints {
t.Run(endpoint, func(t *testing.T) {
testRegisterPprofToServer_AllEndpointsAvailable(t, endpoint)
})
}
}

func testRegisterPprofToServer_AllEndpointsAvailable(t *testing.T, endpoint string) {
// Arrange
cmm := NewCSIMetricsManagerForSidecar(
"fake.csi.driver.io" /* driverName */)
mux := http.NewServeMux()

// Act
cmm.RegisterPprofToServer(mux)

// Assert
request := httptest.NewRequest("GET", endpoint, strings.NewReader(""))
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, request)
resp := rec.Result()

if resp.StatusCode != 200 {
t.Fatalf("%s response status not 200. Response was: %+v", endpoint, resp)
}

contentBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("Failed to parse pprof index response. Response was: %+v Error: %v", resp, err)
}

// Other endpoints return binary data
if endpoint == "/debug/pprof/" {
actualPprofIndex := string(contentBytes)

// This is the exepcted index html page if pprof is running
expectedPprofIndexSubstr := `<body>
/debug/pprof/
<br>
<p>Set debug=1 as a query parameter to export in legacy text format</p>
<br>
Types of profiles available:
<table>`

if ok := strings.Contains(actualPprofIndex, expectedPprofIndexSubstr); !ok {
t.Fatalf("Pprof index returned by end point do not match expectation. Expected: %s \nGot: %s", expectedPprofIndexSubstr, actualPprofIndex)
}
}
}

func TestProcessStartTimeMetricExist(t *testing.T) {
// Arrange
cmm := NewCSIMetricsManagerForSidecar(
Expand Down