Skip to content
quarry Artifact [9c365fb114]
Login

Artifact [9c365fb114]

Artifact 9c365fb114fb141c04369ab2b48575ba5a63285e014163f72ba604b1dd4cb67f:


package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"net/http/pprof"
	"time"
)

// startDebug serves Go's runtime profiles (heap, goroutines, CPU) on
// addr, for an operator on the machine to read with `go tool pprof`.
// It is off unless addr is set, and refuses any address that is not
// loopback: a profile shows request paths, repository names and
// whatever else is in memory, so it is never put where the proxy in
// front could reach it. Its own mux, never http.DefaultServeMux.
func startDebug(addr string) error {
	if addr == "" {
		return nil
	}
	host, _, err := net.SplitHostPort(addr)
	if err != nil {
		return fmt.Errorf("debug address %q: %w", addr, err)
	}
	if ip := net.ParseIP(host); host != "localhost" && (ip == nil || !ip.IsLoopback()) {
		return fmt.Errorf("debug address %q is not loopback: profiles show what is in memory, so they are served on this machine only", addr)
	}
	mux := http.NewServeMux()
	mux.HandleFunc("/debug/pprof/", pprof.Index)
	mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
	mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return fmt.Errorf("debug address %q: %w", addr, err)
	}
	srv := &http.Server{Handler: mux, ReadHeaderTimeout: 10 * time.Second}
	go func() {
		if err := srv.Serve(ln); err != nil && err != http.ErrServerClosed {
			log.Printf("debug: profiles stopped: %v", err)
		}
	}()
	log.Printf("debug: runtime profiles on %s", ln.Addr())
	return nil
}