Documentation
¶
Overview ¶
Package debugserver exposes Go's pprof profiles over a loopback-only HTTP listener, started on demand from an environment variable.
This is compiled into RELEASED binaries, not only dev ones, because the workloads worth profiling are the ones that have been running for days in production — a dev build points at an empty workspace and profiles a healthy process. That decision is what every guard in this file exists to pay for:
- Nothing listens unless QUIL_PPROF is set. No goroutine, no port, no cost.
- The address is refused unless it is LITERALLY loopback, and "localhost" is rewritten to 127.0.0.1 before it reaches net.Listen so the resolver never chooses the bind address. A hostname is not resolved, because a name that resolves to a LAN address today can resolve somewhere else tomorrow.
- The handlers are registered on a PRIVATE mux, so they are reachable only through this listener.
- seconds= is clamped, so one request cannot pin the profiler indefinitely.
What a profile actually exposes, since the guards are only worth what the threat model is: Go's heap profile is a SAMPLED ALLOCATION profile — call stacks and byte counts, not memory contents — so it does not carry terminal buffer contents, and net/http/pprof exposes no endpoint that dumps heap memory. What does leak is narrower and real: /debug/pprof/cmdline is the full argv, which for `quil --remote` names the destination host, and /debug/pprof/goroutine?debug=2 is every goroutine's stack with pointer words and absolute source paths.
The listener is UNAUTHENTICATED, and loopback is a machine boundary rather than a user boundary — quil's IPC socket is chmod 0600, and there is no equivalent for a loopback TCP socket on either platform. So while the port is open, any local account can read the above. That is the reason to set QUIL_PPROF for an investigation rather than leaving it in a shell profile.
The private mux does not make http.DefaultServeMux pristine: importing net/http/pprof runs its init, which registers the same handlers there, and there is no way to import the package without that. It is inert only because nothing in quil serves on DefaultServeMux. If that ever changes, this package must switch to runtime/pprof and hand-written handlers.
Index ¶
Constants ¶
const EnvVar = "QUIL_PPROF"
EnvVar names the environment variable that enables profiling.
Variables ¶
This section is empty.
Functions ¶
func Addr ¶
Addr resolves a QUIL_PPROF value into a listen address.
ok is false with a nil error only for the unset case — "profiling was not asked for" is not a failure. Every other rejection returns an error, because a value the user deliberately set and that silently did nothing is worse than a startup warning.
A value with no colon is a bare port and gets the loopback host prepended. A value WITH a colon must name a loopback host explicitly: ":6060" is the form that binds every interface by accident, so it is refused rather than helpfully corrected.
Types ¶
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is a running pprof listener.
func Start ¶
Start listens on addr and serves pprof.
addr is assumed to have come from Addr; it is re-checked anyway, so a caller that builds an address by hand cannot bypass the loopback rule.
func StartFromEnv ¶
StartFromEnv starts a profiling listener for a QUIL_PPROF value.
Returns (nil, nil) when the variable is unset — the caller's "profiling off" case is not an error and needs no branch of its own.