chore(api): add block and goroutine profiles to support bundle (#9824)
This commit is contained in:
+39
-16
@@ -1165,6 +1165,7 @@ type fileEntry struct {
|
|||||||
|
|
||||||
func (s *service) getSupportBundle(w http.ResponseWriter, r *http.Request) {
|
func (s *service) getSupportBundle(w http.ResponseWriter, r *http.Request) {
|
||||||
var files []fileEntry
|
var files []fileEntry
|
||||||
|
const profilingDuration = 4 * time.Second
|
||||||
|
|
||||||
// Redacted configuration as a JSON
|
// Redacted configuration as a JSON
|
||||||
if jsonConfig, err := json.MarshalIndent(getRedactedConfig(s), "", " "); err != nil {
|
if jsonConfig, err := json.MarshalIndent(getRedactedConfig(s), "", " "); err != nil {
|
||||||
@@ -1231,10 +1232,10 @@ func (s *service) getSupportBundle(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Metrics data as text
|
// Metrics data as text
|
||||||
buf := bytes.NewBuffer(nil)
|
var metricsBuf bytes.Buffer
|
||||||
wr := bufferedResponseWriter{Writer: buf}
|
wr := bufferedResponseWriter{Writer: &metricsBuf}
|
||||||
promhttp.Handler().ServeHTTP(wr, &http.Request{Method: http.MethodGet})
|
promhttp.Handler().ServeHTTP(wr, &http.Request{Method: http.MethodGet})
|
||||||
files = append(files, fileEntry{name: "metrics.txt", data: buf.Bytes()})
|
files = append(files, fileEntry{name: "metrics.txt", data: metricsBuf.Bytes()})
|
||||||
|
|
||||||
// Connection data as JSON
|
// Connection data as JSON
|
||||||
connStats := s.model.ConnectionStats()
|
connStats := s.model.ConnectionStats()
|
||||||
@@ -1244,20 +1245,42 @@ func (s *service) getSupportBundle(w http.ResponseWriter, r *http.Request) {
|
|||||||
files = append(files, fileEntry{name: "connection-stats.json.txt", data: connStatsJSON})
|
files = append(files, fileEntry{name: "connection-stats.json.txt", data: connStatsJSON})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Heap and CPU Proofs as a pprof extension
|
// Write a goroutine profile
|
||||||
var heapBuffer, cpuBuffer bytes.Buffer
|
if p := pprof.Lookup("goroutine"); p != nil {
|
||||||
filename := fmt.Sprintf("syncthing-heap-%s-%s-%s-%s.pprof", runtime.GOOS, runtime.GOARCH, build.Version, time.Now().Format("150405")) // hhmmss
|
var goroutineBuf bytes.Buffer
|
||||||
runtime.GC()
|
_ = p.WriteTo(&goroutineBuf, 0)
|
||||||
if err := pprof.WriteHeapProfile(&heapBuffer); err == nil {
|
filename := fmt.Sprintf("syncthing-goroutines-%s-%s-%s-%s.pprof", runtime.GOOS, runtime.GOARCH, build.Version, time.Now().Format("150405")) // hhmmss
|
||||||
files = append(files, fileEntry{name: filename, data: heapBuffer.Bytes()})
|
files = append(files, fileEntry{name: filename, data: goroutineBuf.Bytes()})
|
||||||
}
|
}
|
||||||
|
|
||||||
const duration = 4 * time.Second
|
// Take a heap profile
|
||||||
filename = fmt.Sprintf("syncthing-cpu-%s-%s-%s-%s.pprof", runtime.GOOS, runtime.GOARCH, build.Version, time.Now().Format("150405")) // hhmmss
|
var heapBuf bytes.Buffer
|
||||||
if err := pprof.StartCPUProfile(&cpuBuffer); err == nil {
|
runtime.GC()
|
||||||
time.Sleep(duration)
|
if err := pprof.WriteHeapProfile(&heapBuf); err == nil {
|
||||||
|
filename := fmt.Sprintf("syncthing-heap-%s-%s-%s-%s.pprof", runtime.GOOS, runtime.GOARCH, build.Version, time.Now().Format("150405")) // hhmmss
|
||||||
|
files = append(files, fileEntry{name: filename, data: heapBuf.Bytes()})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable block profiling
|
||||||
|
runtime.SetBlockProfileRate(1)
|
||||||
|
defer runtime.SetBlockProfileRate(0)
|
||||||
|
|
||||||
|
// Take a CPU profile, waiting for the profiling duration. This also
|
||||||
|
// gives time for the block profile.
|
||||||
|
var cpuBuf bytes.Buffer
|
||||||
|
if err := pprof.StartCPUProfile(&cpuBuf); err == nil {
|
||||||
|
time.Sleep(profilingDuration)
|
||||||
pprof.StopCPUProfile()
|
pprof.StopCPUProfile()
|
||||||
files = append(files, fileEntry{name: filename, data: cpuBuffer.Bytes()})
|
filename := fmt.Sprintf("syncthing-cpu-%s-%s-%s-%s.pprof", runtime.GOOS, runtime.GOARCH, build.Version, time.Now().Format("150405")) // hhmmss
|
||||||
|
files = append(files, fileEntry{name: filename, data: cpuBuf.Bytes()})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the block profile
|
||||||
|
if p := pprof.Lookup("block"); p != nil {
|
||||||
|
var blockBuf bytes.Buffer
|
||||||
|
_ = p.WriteTo(&blockBuf, 0)
|
||||||
|
filename := fmt.Sprintf("syncthing-block-%s-%s-%s-%s.pprof", runtime.GOOS, runtime.GOARCH, build.Version, time.Now().Format("150405")) // hhmmss
|
||||||
|
files = append(files, fileEntry{name: filename, data: blockBuf.Bytes()})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add buffer files to buffer zip
|
// Add buffer files to buffer zip
|
||||||
@@ -1483,7 +1506,7 @@ func (*service) getDeviceID(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (*service) getLang(w http.ResponseWriter, r *http.Request) {
|
func (*service) getLang(w http.ResponseWriter, r *http.Request) {
|
||||||
lang := r.Header.Get("Accept-Language")
|
lang := r.Header.Get("Accept-Language")
|
||||||
var weights = make(map[string]float64)
|
weights := make(map[string]float64)
|
||||||
for _, l := range strings.Split(lang, ",") {
|
for _, l := range strings.Split(lang, ",") {
|
||||||
parts := strings.SplitN(l, ";", 2)
|
parts := strings.SplitN(l, ";", 2)
|
||||||
code := strings.ToLower(strings.TrimSpace(parts[0]))
|
code := strings.ToLower(strings.TrimSpace(parts[0]))
|
||||||
@@ -1502,7 +1525,7 @@ func (*service) getLang(w http.ResponseWriter, r *http.Request) {
|
|||||||
weights[code] = q
|
weights[code] = q
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var langs = make([]string, 0, len(weights))
|
langs := make([]string, 0, len(weights))
|
||||||
for code := range weights {
|
for code := range weights {
|
||||||
langs = append(langs, code)
|
langs = append(langs, code)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user