1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
package cli
import (
"context"
"errors"
"net"
"net/http"
"net/http/pprof"
"runtime"
"sync"
"github.com/mimecast/dtail/internal/io/dlog"
)
// Mutex and block profiling rates. The /debug/pprof/mutex and
// /debug/pprof/block endpoints return empty profiles unless these runtime
// rates are enabled first; they are off by default in the Go runtime.
//
// The values below were validated during task ss0: a mutex profile fraction of
// 5 (report ~1/5 of contention events) and a block profile rate of 100000ns
// (record blocking events longer than 100µs) were sufficient to measure
// outputManager mutex contention — 12.4ms total delay across three concurrent
// 100MB server-mode dcats, i.e. NOT a bottleneck; an idle follow session
// costs 0 CPU ticks/10s because the 1ms read poll only runs while direct output is
// active and the EOF-ack drops it back to 1s polling. Documented here so the
// locking design is not re-litigated.
const (
mutexProfileFraction = 5
blockProfileRateNanos = 100000
)
// PProfServer owns a dedicated pprof HTTP server lifecycle.
type PProfServer struct {
listener net.Listener
server *http.Server
done chan struct{}
}
// NewPProfServer creates a pprof HTTP server bound to address.
func NewPProfServer(address string) (*PProfServer, error) {
listener, err := net.Listen("tcp", address)
if err != nil {
return nil, err
}
return &PProfServer{
listener: listener,
server: &http.Server{
Handler: newPProfServeMux(),
},
done: make(chan struct{}),
}, nil
}
// EnableProfilingRates turns on mutex and block profiling collection so that
// the /debug/pprof/mutex and /debug/pprof/block endpoints actually contain
// samples. Without this the Go runtime keeps both rates at zero and those
// endpoints report empty profiles. Only call this when pprof is enabled so it
// costs nothing in the common (no --pprof) case.
func EnableProfilingRates() {
runtime.SetMutexProfileFraction(mutexProfileFraction)
runtime.SetBlockProfileRate(blockProfileRateNanos)
}
// Address returns the bound pprof listener address.
func (s *PProfServer) Address() string {
if s == nil || s.listener == nil {
return ""
}
return s.listener.Addr().String()
}
// Start serves the pprof HTTP endpoints until shutdown.
func (s *PProfServer) Start(wg *sync.WaitGroup) {
if s == nil {
return
}
if wg != nil {
wg.Add(1)
}
go func() {
if wg != nil {
defer wg.Done()
}
defer close(s.done)
if err := s.server.Serve(s.listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
dlog.Client.Error("PProf server exited", err)
}
}()
}
// Shutdown stops the pprof HTTP server and waits for Serve to return.
func (s *PProfServer) Shutdown(ctx context.Context) error {
if s == nil {
return nil
}
err := s.server.Shutdown(ctx)
<-s.done
return err
}
func newPProfServeMux() *http.ServeMux {
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)
for _, name := range []string{"allocs", "block", "goroutine", "heap", "mutex", "threadcreate"} {
mux.Handle("/debug/pprof/"+name, pprof.Handler(name))
}
return mux
}
|