syncthing/cmd/syncthing/usage_report.go

183 lines
4.3 KiB
Go
Raw Normal View History

2014-11-16 21:13:20 +01:00
// Copyright (C) 2014 The Syncthing Authors.
2014-09-29 21:43:32 +02:00
//
2015-03-07 21:36:35 +01:00
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
2014-09-04 08:31:38 +02:00
2014-06-11 20:04:23 +02:00
package main
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"encoding/json"
"net"
2014-06-11 20:04:23 +02:00
"net/http"
"runtime"
"time"
"github.com/syncthing/syncthing/internal/config"
2014-09-22 21:42:11 +02:00
"github.com/syncthing/syncthing/internal/model"
"github.com/thejerf/suture"
2014-06-11 20:04:23 +02:00
)
// Current version number of the usage report, for acceptance purposes. If
// fields are added or changed this integer must be incremented so that users
// are prompted for acceptance of the new report.
const usageReportVersion = 1
type usageReportingManager struct {
model *model.Model
sup *suture.Supervisor
}
func newUsageReportingManager(m *model.Model, cfg *config.Wrapper) *usageReportingManager {
mgr := &usageReportingManager{
model: m,
}
// Start UR if it's enabled.
mgr.Changed(cfg.Raw())
// Listen to future config changes so that we can start and stop as
// appropriate.
cfg.Subscribe(mgr)
return mgr
}
func (m *usageReportingManager) Changed(cfg config.Configuration) error {
if cfg.Options.URAccepted >= usageReportVersion && m.sup == nil {
// Usage reporting was turned on; lets start it.
svc := &usageReportingService{
model: m.model,
}
m.sup = suture.NewSimple("usageReporting")
m.sup.Add(svc)
m.sup.ServeBackground()
} else if cfg.Options.URAccepted < usageReportVersion && m.sup != nil {
// Usage reporting was turned off
m.sup.Stop()
m.sup = nil
}
return nil
}
2014-06-11 20:04:23 +02:00
// reportData returns the data to be sent in a usage report. It's used in
// various places, so not part of the usageReportingSvc object.
2014-06-11 20:04:23 +02:00
func reportData(m *model.Model) map[string]interface{} {
res := make(map[string]interface{})
res["uniqueID"] = cfg.Options().URUniqueID
2014-06-11 20:04:23 +02:00
res["version"] = Version
2014-06-21 09:43:12 +02:00
res["longVersion"] = LongVersion
2014-06-11 20:04:23 +02:00
res["platform"] = runtime.GOOS + "-" + runtime.GOARCH
res["numFolders"] = len(cfg.Folders())
res["numDevices"] = len(cfg.Devices())
2014-06-11 20:04:23 +02:00
var totFiles, maxFiles int
var totBytes, maxBytes int64
for folderID := range cfg.Folders() {
files, _, bytes := m.GlobalSize(folderID)
2014-06-11 20:04:23 +02:00
totFiles += files
totBytes += bytes
if files > maxFiles {
maxFiles = files
}
if bytes > maxBytes {
maxBytes = bytes
}
}
res["totFiles"] = totFiles
res["folderMaxFiles"] = maxFiles
2014-06-11 20:04:23 +02:00
res["totMiB"] = totBytes / 1024 / 1024
res["folderMaxMiB"] = maxBytes / 1024 / 1024
2014-06-11 20:04:23 +02:00
var mem runtime.MemStats
runtime.ReadMemStats(&mem)
res["memoryUsageMiB"] = (mem.Sys - mem.HeapReleased) / 1024 / 1024
2014-06-11 20:04:23 +02:00
var perf float64
for i := 0; i < 5; i++ {
p := cpuBench()
if p > perf {
perf = p
}
}
res["sha256Perf"] = perf
bytes, err := memorySize()
if err == nil {
res["memorySize"] = bytes / 1024 / 1024
}
2014-06-11 20:04:23 +02:00
return res
}
type usageReportingService struct {
model *model.Model
stop chan struct{}
}
func (s *usageReportingService) sendUsageReport() error {
d := reportData(s.model)
2014-06-11 20:04:23 +02:00
var b bytes.Buffer
json.NewEncoder(&b).Encode(d)
var client = http.DefaultClient
2014-06-13 20:45:57 +02:00
if BuildEnv == "android" {
// This works around the lack of DNS resolution on Android... :(
tr := &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
return net.Dial(network, "194.126.249.13:443")
},
}
client = &http.Client{Transport: tr}
}
_, err := client.Post("https://data.syncthing.net/newdata", "application/json", &b)
2014-06-11 20:04:23 +02:00
return err
}
func (s *usageReportingService) Serve() {
s.stop = make(chan struct{})
2014-06-11 20:04:23 +02:00
l.Infoln("Starting usage reporting")
defer l.Infoln("Stopping usage reporting")
t := time.NewTimer(10 * time.Minute) // time to initial report at start
2014-06-11 20:04:23 +02:00
for {
select {
case <-s.stop:
return
2014-06-11 20:04:23 +02:00
case <-t.C:
err := s.sendUsageReport()
if err != nil {
l.Infoln("Usage report:", err)
}
t.Reset(24 * time.Hour) // next report tomorrow
2014-06-11 20:04:23 +02:00
}
}
}
func (s *usageReportingService) Stop() {
close(s.stop)
2014-06-11 20:04:23 +02:00
}
// cpuBench returns CPU performance as a measure of single threaded SHA-256 MiB/s
2014-06-11 20:04:23 +02:00
func cpuBench() float64 {
chunkSize := 100 * 1 << 10
h := sha256.New()
bs := make([]byte, chunkSize)
rand.Reader.Read(bs)
t0 := time.Now()
b := 0
for time.Since(t0) < 125*time.Millisecond {
h.Write(bs)
b += chunkSize
}
h.Sum(nil)
d := time.Since(t0)
return float64(int(float64(b)/d.Seconds()/(1<<20)*100)) / 100
}