Browse Source

cmd/stbench: Add utility to run benchmark tests

Jakob Borg 9 years ago
parent
commit
f706d3c393
2 changed files with 192 additions and 0 deletions
  1. 143 0
      cmd/stbench/main.go
  2. 49 0
      lib/rc/rc.go

+ 143 - 0
cmd/stbench/main.go

@@ -0,0 +1,143 @@
+// Copyright (C) 2016 The Syncthing Authors.
+//
+// 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/.
+
+// This doesn't build on Windows due to the Rusage stuff.
+
+// +build !windows
+
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"runtime"
+	"syscall"
+	"time"
+
+	"github.com/syncthing/syncthing/lib/rc"
+)
+
+var homeDir = "h1"
+var syncthingBin = "./bin/syncthing"
+var test = "scan"
+
+func main() {
+	flag.StringVar(&homeDir, "home", homeDir, "Home directory location")
+	flag.StringVar(&syncthingBin, "bin", syncthingBin, "Binary location")
+	flag.StringVar(&test, "test", test, "Test to run")
+	flag.Parse()
+
+	switch test {
+	case "scan":
+		// scan measures the resource usage required to perform the initial
+		// scan, without cleaning away the database first.
+		testScan()
+	}
+}
+
+// testScan starts a process and reports on the resource usage required to
+// perform the initial scan.
+func testScan() {
+	log.Println("Starting...")
+	p := rc.NewProcess("127.0.0.1:8081")
+	if err := p.Start(syncthingBin, "-home", homeDir, "-no-browser"); err != nil {
+		log.Println(err)
+		return
+	}
+	defer p.Stop()
+
+	wallTime := awaitScanComplete(p)
+
+	report(p, wallTime)
+}
+
+// awaitScanComplete waits for a folder to transition idle->scanning and
+// then scanning->idle and returns the time taken for the scan.
+func awaitScanComplete(p *rc.Process) time.Duration {
+	log.Println("Awaiting scan completion...")
+	var t0, t1 time.Time
+	lastEvent := 0
+loop:
+	for {
+		evs, err := p.Events(lastEvent)
+		if err != nil {
+			continue
+		}
+
+		for _, ev := range evs {
+			if ev.Type == "StateChanged" {
+				data := ev.Data.(map[string]interface{})
+				log.Println(ev)
+
+				if data["to"].(string) == "scanning" {
+					t0 = ev.Time
+					continue
+				}
+
+				if !t0.IsZero() && data["to"].(string) == "idle" {
+					t1 = ev.Time
+					break loop
+				}
+			}
+			lastEvent = ev.ID
+		}
+
+		time.Sleep(250 * time.Millisecond)
+	}
+
+	return t1.Sub(t0)
+}
+
+// report stops the given process and reports on it's resource usage in two
+// ways: human readable to stderr, and CSV to stdout.
+func report(p *rc.Process, wallTime time.Duration) {
+	sv, err := p.SystemVersion()
+	if err != nil {
+		log.Println(err)
+		return
+	}
+
+	ss, err := p.SystemStatus()
+	if err != nil {
+		log.Println(err)
+		return
+	}
+
+	proc, err := p.Stop()
+	if err != nil {
+		return
+	}
+
+	rusage, ok := proc.SysUsage().(*syscall.Rusage)
+	if !ok {
+		return
+	}
+
+	log.Println("Version:", sv.Version)
+	log.Println("Alloc:", ss.Alloc/1024, "KiB")
+	log.Println("Sys:", ss.Sys/1024, "KiB")
+	log.Println("Goroutines:", ss.Goroutines)
+	log.Println("Wall time:", wallTime)
+	log.Println("Utime:", time.Duration(rusage.Utime.Nano()))
+	log.Println("Stime:", time.Duration(rusage.Stime.Nano()))
+	if runtime.GOOS == "darwin" {
+		// Darwin reports in bytes, Linux seems to report in KiB even
+		// though the manpage says otherwise.
+		rusage.Maxrss /= 1024
+	}
+	log.Println("MaxRSS:", rusage.Maxrss, "KiB")
+
+	fmt.Printf("%s,%d,%d,%d,%.02f,%.02f,%.02f,%d\n",
+		sv.Version,
+		ss.Alloc/1024,
+		ss.Sys/1024,
+		ss.Goroutines,
+		wallTime.Seconds(),
+		time.Duration(rusage.Utime.Nano()).Seconds(),
+		time.Duration(rusage.Stime.Nano()).Seconds(),
+		rusage.Maxrss)
+}

+ 49 - 0
lib/rc/rc.go

@@ -574,3 +574,52 @@ func (p *Process) Connections() (map[string]ConnectionStats, error) {
 
 	return res, nil
 }
+
+type SystemStatus struct {
+	Alloc         int64
+	CPUPercent    float64
+	Goroutines    int
+	MyID          protocol.DeviceID
+	PathSeparator string
+	StartTime     time.Time
+	Sys           int64
+	Themes        []string
+	Tilde         string
+	Uptime        int
+}
+
+func (p *Process) SystemStatus() (SystemStatus, error) {
+	bs, err := p.Get("/rest/system/status")
+	if err != nil {
+		return SystemStatus{}, err
+	}
+
+	var res SystemStatus
+	if err := json.Unmarshal(bs, &res); err != nil {
+		return SystemStatus{}, err
+	}
+
+	return res, nil
+}
+
+type SystemVersion struct {
+	Arch        string
+	Codename    string
+	LongVersion string
+	OS          string
+	Version     string
+}
+
+func (p *Process) SystemVersion() (SystemVersion, error) {
+	bs, err := p.Get("/rest/system/version")
+	if err != nil {
+		return SystemVersion{}, err
+	}
+
+	var res SystemVersion
+	if err := json.Unmarshal(bs, &res); err != nil {
+		return SystemVersion{}, err
+	}
+
+	return res, nil
+}