Explorar o código

chore(api): add block and goroutine profiles to support bundle (#9824)

Jakob Borg hai 11 meses
pai
achega
7eaf843de2
Modificáronse 1 ficheiros con 38 adicións e 15 borrados
  1. 38 15
      lib/api/api.go

+ 38 - 15
lib/api/api.go

@@ -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)
-	wr := bufferedResponseWriter{Writer: buf}
+	var metricsBuf bytes.Buffer
+	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
-	var heapBuffer, cpuBuffer bytes.Buffer
-	filename := fmt.Sprintf("syncthing-heap-%s-%s-%s-%s.pprof", runtime.GOOS, runtime.GOARCH, build.Version, time.Now().Format("150405")) // hhmmss
+	// Write a goroutine profile
+	if p := pprof.Lookup("goroutine"); p != nil {
+		var goroutineBuf bytes.Buffer
+		_ = p.WriteTo(&goroutineBuf, 0)
+		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: goroutineBuf.Bytes()})
+	}
+
+	// Take a heap profile
+	var heapBuf bytes.Buffer
 	runtime.GC()
 	runtime.GC()
-	if err := pprof.WriteHeapProfile(&heapBuffer); err == nil {
-		files = append(files, fileEntry{name: filename, data: heapBuffer.Bytes()})
+	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()})
 	}
 	}
 
 
-	const duration = 4 * time.Second
-	filename = fmt.Sprintf("syncthing-cpu-%s-%s-%s-%s.pprof", runtime.GOOS, runtime.GOARCH, build.Version, time.Now().Format("150405")) // hhmmss
-	if err := pprof.StartCPUProfile(&cpuBuffer); err == nil {
-		time.Sleep(duration)
+	// 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)
 	}
 	}