/
debug_server.go
118 lines (107 loc) · 3.46 KB
/
debug_server.go
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
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2023-present Datadog, Inc.
//go:build !serverless
package api
import (
"context"
"expvar"
"fmt"
"net"
"net/http"
"net/http/pprof"
"runtime"
"strconv"
"time"
"github.com/DataDog/datadog-agent/pkg/trace/config"
"github.com/DataDog/datadog-agent/pkg/trace/log"
)
const (
defaultTimeout = 5 * time.Second
defaultShutdownDeadline = 5 * time.Second
)
// DebugServer serves /debug/* endpoints
type DebugServer struct {
conf *config.AgentConfig
server *http.Server
mux *http.ServeMux
}
// NewDebugServer returns a debug server
func NewDebugServer(conf *config.AgentConfig) *DebugServer {
return &DebugServer{
conf: conf,
mux: http.NewServeMux(),
}
}
// Start configures and starts the http server
func (ds *DebugServer) Start() {
if ds.conf.DebugServerPort == 0 {
log.Debug("Debug server is disabled by config (apm_config.debug.port: 0).")
return
}
ds.server = &http.Server{
ReadTimeout: defaultTimeout,
WriteTimeout: defaultTimeout,
Handler: ds.setupMux(),
}
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", ds.conf.DebugServerPort))
if err != nil {
log.Errorf("Error creating debug server listener: %s", err)
return
}
go func() {
if err := ds.server.Serve(listener); err != nil && err != http.ErrServerClosed {
log.Errorf("Could not start debug server: %s. Debug server disabled.", err)
}
}()
}
// Stop shuts down the debug server
func (ds *DebugServer) Stop() {
if ds.server == nil {
return
}
ctx, cancel := context.WithTimeout(context.Background(), defaultShutdownDeadline)
defer cancel()
if err := ds.server.Shutdown(ctx); err != nil {
log.Errorf("Error stopping debug server: %s", err)
}
}
// AddRoute adds a route to the DebugServer
func (ds *DebugServer) AddRoute(route string, handler http.Handler) {
ds.mux.Handle(route, handler)
}
func (ds *DebugServer) setupMux() *http.ServeMux {
ds.mux.HandleFunc("/debug/pprof/", pprof.Index)
ds.mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
ds.mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
ds.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
ds.mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
ds.mux.HandleFunc("/debug/blockrate", func(w http.ResponseWriter, r *http.Request) {
// this endpoint calls runtime.SetBlockProfileRate(v), where v is an optional
// query string parameter defaulting to 10000 (1 sample per 10μs blocked).
rate := 10000
v := r.URL.Query().Get("v")
if v != "" {
n, err := strconv.Atoi(v)
if err != nil {
http.Error(w, "v must be an integer", http.StatusBadRequest)
return
}
rate = n
}
runtime.SetBlockProfileRate(rate)
fmt.Fprintf(w, "Block profile rate set to %d. It will automatically be disabled again after calling /debug/pprof/block\n", rate)
})
ds.mux.HandleFunc("/debug/pprof/block", func(w http.ResponseWriter, r *http.Request) {
// serve the block profile and reset the rate to 0.
pprof.Handler("block").ServeHTTP(w, r)
runtime.SetBlockProfileRate(0)
})
ds.mux.Handle("/debug/vars", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// allow the GUI to call this endpoint so that the status can be reported
w.Header().Set("Access-Control-Allow-Origin", "http://127.0.0.1:"+ds.conf.GUIPort)
expvar.Handler().ServeHTTP(w, req)
}))
return ds.mux
}