| 15 | ) |
| 16 | |
| 17 | func pprofUIHandler(w http.ResponseWriter, r *http.Request) { |
| 18 | ctx := r.Context() |
| 19 | vars := mux.Vars(r) |
| 20 | |
| 21 | f, err := os.CreateTemp("", "pprof.*.pb") |
| 22 | if err != nil { |
| 23 | log(ctx, err).Error("error creating temporary file") |
| 24 | http.Error(w, err.Error(), http.StatusInternalServerError) |
| 25 | return |
| 26 | } |
| 27 | log(ctx, err).WithField("file", f.Name()).Debug("writing pprof to temp file") |
| 28 | defer removeFile(ctx, f.Name()) |
| 29 | |
| 30 | q := r.URL.Query() |
| 31 | q.Del("debug") // debug doesn't make sense for UI |
| 32 | r.URL.RawQuery = q.Encode() |
| 33 | |
| 34 | // Record what would normally be served to the user in a file such that it can be passed to the UI renderer |
| 35 | rec := httptest.NewRecorder() |
| 36 | profile := vars["profile"] |
| 37 | if profile == "profile" { |
| 38 | httppprof.Profile(rec, r) |
| 39 | } else { |
| 40 | // Check validity of the profile |
| 41 | p := pprof.Lookup(profile) |
| 42 | if p == nil { |
| 43 | // The /debug/pprof/ui page has links to all the profiles, but some are not supported, so redirect to non-ui. |
| 44 | log(ctx, nil).WithField("profile", profile).Warn("unknown profile") |
| 45 | url := strings.Replace(r.URL.Path, "/ui/", "/", 1) |
| 46 | http.Redirect(w, r, url, http.StatusMovedPermanently) |
| 47 | return |
| 48 | } |
| 49 | |
| 50 | httppprof.Handler(profile).ServeHTTP(rec, r) |
| 51 | } |
| 52 | |
| 53 | if rec.Code != 0 && rec.Code != http.StatusOK { |
| 54 | log(ctx, err).Error("error recording pprof") |
| 55 | http.Error(w, rec.Body.String(), rec.Code) |
| 56 | return |
| 57 | } |
| 58 | if _, err = rec.Body.WriteTo(f); err != nil { |
| 59 | log(ctx, err).Error("error closing file") |
| 60 | http.Error(w, err.Error(), http.StatusInternalServerError) |
| 61 | return |
| 62 | } |
| 63 | |
| 64 | if err := serveUI(ctx, w, r, f.Name()); err != nil { |
| 65 | log(ctx, err).Warn("error serving pprof ui") |
| 66 | http.Error(w, err.Error(), http.StatusInternalServerError) |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | func serveUI(ctx context.Context, w http.ResponseWriter, r *http.Request, inputFile string) error { |
| 71 | vars := mux.Vars(r) |