usage_report.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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 http://mozilla.org/MPL/2.0/.
  6. package main
  7. import (
  8. "bytes"
  9. "crypto/rand"
  10. "crypto/sha256"
  11. "encoding/json"
  12. "net"
  13. "net/http"
  14. "runtime"
  15. "time"
  16. "github.com/syncthing/syncthing/internal/model"
  17. )
  18. // Current version number of the usage report, for acceptance purposes. If
  19. // fields are added or changed this integer must be incremented so that users
  20. // are prompted for acceptance of the new report.
  21. const usageReportVersion = 1
  22. var stopUsageReportingCh = make(chan struct{})
  23. func reportData(m *model.Model) map[string]interface{} {
  24. res := make(map[string]interface{})
  25. res["uniqueID"] = cfg.Options().URUniqueID
  26. res["version"] = Version
  27. res["longVersion"] = LongVersion
  28. res["platform"] = runtime.GOOS + "-" + runtime.GOARCH
  29. res["numFolders"] = len(cfg.Folders())
  30. res["numDevices"] = len(cfg.Devices())
  31. var totFiles, maxFiles int
  32. var totBytes, maxBytes int64
  33. for folderID := range cfg.Folders() {
  34. files, _, bytes := m.GlobalSize(folderID)
  35. totFiles += files
  36. totBytes += bytes
  37. if files > maxFiles {
  38. maxFiles = files
  39. }
  40. if bytes > maxBytes {
  41. maxBytes = bytes
  42. }
  43. }
  44. res["totFiles"] = totFiles
  45. res["folderMaxFiles"] = maxFiles
  46. res["totMiB"] = totBytes / 1024 / 1024
  47. res["folderMaxMiB"] = maxBytes / 1024 / 1024
  48. var mem runtime.MemStats
  49. runtime.ReadMemStats(&mem)
  50. res["memoryUsageMiB"] = (mem.Sys - mem.HeapReleased) / 1024 / 1024
  51. var perf float64
  52. for i := 0; i < 5; i++ {
  53. p := cpuBench()
  54. if p > perf {
  55. perf = p
  56. }
  57. }
  58. res["sha256Perf"] = perf
  59. bytes, err := memorySize()
  60. if err == nil {
  61. res["memorySize"] = bytes / 1024 / 1024
  62. }
  63. return res
  64. }
  65. func sendUsageReport(m *model.Model) error {
  66. d := reportData(m)
  67. var b bytes.Buffer
  68. json.NewEncoder(&b).Encode(d)
  69. var client = http.DefaultClient
  70. if BuildEnv == "android" {
  71. // This works around the lack of DNS resolution on Android... :(
  72. tr := &http.Transport{
  73. Dial: func(network, addr string) (net.Conn, error) {
  74. return net.Dial(network, "194.126.249.13:443")
  75. },
  76. }
  77. client = &http.Client{Transport: tr}
  78. }
  79. _, err := client.Post("https://data.syncthing.net/newdata", "application/json", &b)
  80. return err
  81. }
  82. func usageReportingLoop(m *model.Model) {
  83. l.Infoln("Starting usage reporting")
  84. t := time.NewTicker(86400 * time.Second)
  85. loop:
  86. for {
  87. select {
  88. case <-stopUsageReportingCh:
  89. break loop
  90. case <-t.C:
  91. err := sendUsageReport(m)
  92. if err != nil {
  93. l.Infoln("Usage report:", err)
  94. }
  95. }
  96. }
  97. l.Infoln("Stopping usage reporting")
  98. }
  99. func stopUsageReporting() {
  100. select {
  101. case stopUsageReportingCh <- struct{}{}:
  102. default:
  103. }
  104. }
  105. // Returns CPU performance as a measure of single threaded SHA-256 MiB/s
  106. func cpuBench() float64 {
  107. chunkSize := 100 * 1 << 10
  108. h := sha256.New()
  109. bs := make([]byte, chunkSize)
  110. rand.Reader.Read(bs)
  111. t0 := time.Now()
  112. b := 0
  113. for time.Since(t0) < 125*time.Millisecond {
  114. h.Write(bs)
  115. b += chunkSize
  116. }
  117. h.Sum(nil)
  118. d := time.Since(t0)
  119. return float64(int(float64(b)/d.Seconds()/(1<<20)*100)) / 100
  120. }