perfstats_unix.go 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // Copyright (C) 2014 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. //go:build !solaris && !windows
  7. // +build !solaris,!windows
  8. package main
  9. import (
  10. "fmt"
  11. "os"
  12. "runtime"
  13. "syscall"
  14. "time"
  15. "github.com/syncthing/syncthing/lib/locations"
  16. "github.com/syncthing/syncthing/lib/protocol"
  17. "golang.org/x/exp/constraints"
  18. )
  19. func startPerfStats() {
  20. go savePerfStats(fmt.Sprintf("perfstats-%d.csv", syscall.Getpid()))
  21. }
  22. func savePerfStats(file string) {
  23. fd, err := os.Create(file)
  24. if err != nil {
  25. panic(err)
  26. }
  27. var prevTime time.Time
  28. var curRus, prevRus syscall.Rusage
  29. var curMem, prevMem runtime.MemStats
  30. var prevIn, prevOut int64
  31. t0 := time.Now()
  32. syscall.Getrusage(syscall.RUSAGE_SELF, &prevRus)
  33. runtime.ReadMemStats(&prevMem)
  34. fmt.Fprintf(fd, "TIME_S\tCPU_S\tHEAP_KIB\tRSS_KIB\tNETIN_KBPS\tNETOUT_KBPS\tDBSIZE_KIB\n")
  35. for t := range time.NewTicker(250 * time.Millisecond).C {
  36. syscall.Getrusage(syscall.RUSAGE_SELF, &curRus)
  37. runtime.ReadMemStats(&curMem)
  38. in, out := protocol.TotalInOut()
  39. timeDiff := t.Sub(prevTime)
  40. fmt.Fprintf(fd, "%.03f\t%f\t%d\t%d\t%.0f\t%.0f\t%d\n",
  41. t.Sub(t0).Seconds(),
  42. rate(cpusec(&prevRus), cpusec(&curRus), timeDiff, 1),
  43. (curMem.Sys-curMem.HeapReleased)/1024,
  44. curRus.Maxrss/1024,
  45. rate(prevIn, in, timeDiff, 1e3),
  46. rate(prevOut, out, timeDiff, 1e3),
  47. dirsize(locations.Get(locations.Database))/1024,
  48. )
  49. prevTime = t
  50. prevRus = curRus
  51. prevMem = curMem
  52. prevIn, prevOut = in, out
  53. }
  54. }
  55. func cpusec(r *syscall.Rusage) float64 {
  56. return float64(r.Utime.Nano()+r.Stime.Nano()) / float64(time.Second)
  57. }
  58. type number interface {
  59. constraints.Float | constraints.Integer
  60. }
  61. func rate[T number](prev, cur T, d time.Duration, div float64) float64 {
  62. diff := cur - prev
  63. rate := float64(diff) / d.Seconds() / div
  64. return rate
  65. }
  66. func dirsize(location string) int64 {
  67. entries, err := os.ReadDir(location)
  68. if err != nil {
  69. return 0
  70. }
  71. var size int64
  72. for _, entry := range entries {
  73. fi, err := entry.Info()
  74. if err != nil {
  75. continue
  76. }
  77. size += fi.Size()
  78. }
  79. return size
  80. }