Commit 7d6bbd1e authored by Quentin Smith's avatar Quentin Smith

all: improve local server workflow

- document local servers in README
- rename local servers to "localperf" and "localperfdata" so one can
  "go get" them.
- find static/templates dirs automatically
- allow configuring a persistent database in localperfdata

Change-Id: I4e62f23c38be6978f091ccbbda8002d9f588b8a4
Reviewed-on: https://go-review.googlesource.com/37717Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent 52d96bae
This subrepository holds the source for various packages and tools
related to performance measurement, storage, and analysis.
To submit changes to this repository, see http://golang.org/doc/contribute.html.
# Go performance measurement, storage, and analysis tools
This subrepository holds the source for various packages and tools
related to performance measurement, storage, and analysis.
[cmd/benchstat](cmd/benchstat) contains a command-line tool that
computes and compares statistics about benchmarks.
[cmd/benchsave](cmd/benchsave) contains a command-line tool for
publishing benchmark results.
[storage](storage) contains the https://perfdata.golang.org/ benchmark
result storage system.
[analysis](analysis) contains the https://perf.golang.org/ benchmark
result analysis system.
Both storage and analysis can be run locally; the following commands will run
the complete stack on your machine with an in-memory datastore.
```
go get -u golang.org/x/perf/storage/localperfdata
go get -u golang.org/x/perf/analysis/localperf
localperfdata -addr=:8081 -view_url_base=http://localhost:8080/search?q=upload: &
localperf -addr=:8080 -storage=localhost:8081
```
The storage system is designed to have a
[standardized API](storage/appengine/static/index.html), and we
encourage additional analysis tools to be written against the API. A
client can be found in [storage/client](storage/client).
--
Contributions to Go are appreciated. See http://golang.org/doc/contribute.html.
* Bugs can be filed at the [Go issue tracker](https://golang.org/issue/new?title=x/perf:+).
......@@ -16,6 +16,10 @@ import (
type App struct {
// StorageClient is used to talk to the storage server.
StorageClient *storage.Client
// BaseDir is the directory containing the "template" directory.
// If empty, the current directory will be used.
BaseDir string
}
// RegisterOnMux registers the app's URLs on mux.
......
......@@ -11,6 +11,7 @@ import (
"html/template"
"io/ioutil"
"net/http"
"path/filepath"
"sort"
"strconv"
"strings"
......@@ -140,7 +141,7 @@ func (a *App) compare(w http.ResponseWriter, r *http.Request) {
q := r.Form.Get("q")
tmpl, err := ioutil.ReadFile("template/compare.html")
tmpl, err := ioutil.ReadFile(filepath.Join(a.BaseDir, "template/compare.html"))
if err != nil {
http.Error(w, err.Error(), 500)
return
......
......@@ -8,6 +8,7 @@ import (
"html/template"
"io/ioutil"
"net/http"
"path/filepath"
"golang.org/x/perf/storage"
)
......@@ -16,7 +17,7 @@ import (
func (a *App) index(w http.ResponseWriter, r *http.Request) {
ctx := requestContext(r)
tmpl, err := ioutil.ReadFile("template/index.html")
tmpl, err := ioutil.ReadFile(filepath.Join(a.BaseDir, "template/index.html"))
if err != nil {
http.Error(w, err.Error(), 500)
return
......
......@@ -14,6 +14,7 @@ import (
"io/ioutil"
"math"
"net/http"
"path/filepath"
"sort"
"strconv"
"strings"
......@@ -38,7 +39,7 @@ func (a *App) trend(w http.ResponseWriter, r *http.Request) {
q := r.Form.Get("q")
tmpl, err := ioutil.ReadFile("template/trend.html")
tmpl, err := ioutil.ReadFile(filepath.Join(a.BaseDir, "template/trend.html"))
if err != nil {
http.Error(w, err.Error(), 500)
return
......
......@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Localserver runs an HTTP server for benchmark analysis.
// Localperf runs an HTTP server for benchmark analysis.
//
// Usage:
//
// localserver [-addr address] [-storage url]
// localperf [-addr address] [-storage url] [-base_dir ../appengine]
package main
import (
......@@ -17,31 +17,41 @@ import (
"os"
"golang.org/x/perf/analysis/app"
"golang.org/x/perf/internal/basedir"
"golang.org/x/perf/storage"
)
var (
addr = flag.String("addr", "localhost:8080", "serve HTTP on `address`")
storageURL = flag.String("storage", "https://perfdata.golang.org", "storage server base `url`")
baseDir = flag.String("base_dir", basedir.Find("golang.org/x/perf/analysis/appengine"), "base `directory` for templates")
)
func usage() {
fmt.Fprintf(os.Stderr, `Usage of localserver:
localserver [flags]
fmt.Fprintf(os.Stderr, `Usage of localperf:
localperf [flags]
`)
flag.PrintDefaults()
os.Exit(2)
}
func main() {
log.SetPrefix("localserver: ")
log.SetPrefix("localperf: ")
flag.Usage = usage
flag.Parse()
if flag.NArg() != 0 {
flag.Usage()
}
app := &app.App{StorageClient: &storage.Client{BaseURL: *storageURL}}
if *baseDir == "" {
log.Print("base_dir is required and could not be automatically found")
flag.Usage()
}
app := &app.App{
StorageClient: &storage.Client{BaseURL: *storageURL},
BaseDir: *baseDir,
}
app.RegisterOnMux(http.DefaultServeMux)
log.Printf("Listening on %s", *addr)
......
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package basedir finds templates and static files associated with a binary.
package basedir
import (
"bytes"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
)
// Find locates a directory for the given package.
// pkg should be the directory that contains the templates and/or static directories.
// If pkg cannot be found, an empty string will be returned.
func Find(pkg string) string {
cmd := exec.Command("go", "list", "-e", "-f", "{{.Dir}}", pkg)
if out, err := cmd.Output(); err == nil && len(out) > 0 {
return string(bytes.TrimRight(out, "\r\n"))
}
gopath := os.Getenv("GOPATH")
if gopath == "" {
gopath = defaultGOPATH()
}
if gopath != "" {
for _, dir := range strings.Split(gopath, ":") {
p := filepath.Join(dir, pkg)
if _, err := os.Stat(p); err == nil {
return p
}
}
}
return ""
}
// Copied from go/build/build.go
func defaultGOPATH() string {
env := "HOME"
if runtime.GOOS == "windows" {
env = "USERPROFILE"
} else if runtime.GOOS == "plan9" {
env = "home"
}
if home := os.Getenv(env); home != "" {
def := filepath.Join(home, "go")
if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
// Don't set the default GOPATH to GOROOT,
// as that will trigger warnings from the go tool.
return ""
}
return def
}
return ""
}
......@@ -9,6 +9,7 @@ package app
import (
"errors"
"net/http"
"path/filepath"
"golang.org/x/perf/storage/db"
"golang.org/x/perf/storage/fs"
......@@ -30,6 +31,10 @@ type App struct {
// "viewurl" in the response from /upload. If it is non-empty,
// the upload ID will be appended to ViewURLBase.
ViewURLBase string
// BaseDir is the directory containing the "template" directory.
// If empty, the current directory will be used.
BaseDir string
}
// ErrResponseWritten can be returned by App.Auth to abort the normal /upload handling.
......@@ -46,5 +51,5 @@ func (a *App) RegisterOnMux(mux *http.ServeMux) {
// index serves the readme on /
func (a *App) index(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "static/index.html")
http.ServeFile(w, r, filepath.Join(a.BaseDir, "static/index.html"))
}
......@@ -12,6 +12,7 @@ import (
"mime/multipart"
"net/http"
"net/url"
"path/filepath"
"sort"
"strings"
"time"
......@@ -36,7 +37,7 @@ func (a *App) upload(w http.ResponseWriter, r *http.Request) {
}
if r.Method == http.MethodGet {
http.ServeFile(w, r, "static/upload.html")
http.ServeFile(w, r, filepath.Join(a.BaseDir, "static/upload.html"))
return
}
if r.Method != http.MethodPost {
......
......@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Localperfdata runs an HTTP server for benchmark storage.
//
// Usage:
//
// localperfdata [-addr address] [-view_url_base url] [-base_dir ../appengine] [-dsn file.db]
package main
import (
......@@ -9,6 +14,7 @@ import (
"log"
"net/http"
"golang.org/x/perf/internal/basedir"
"golang.org/x/perf/storage/app"
"golang.org/x/perf/storage/db"
_ "golang.org/x/perf/storage/db/sqlite3"
......@@ -18,12 +24,19 @@ import (
var (
addr = flag.String("addr", ":8080", "serve HTTP on `address`")
viewURLBase = flag.String("view_url_base", "", "/upload response with `URL` for viewing")
dsn = flag.String("dsn", ":memory:", "sqlite `dsn`")
baseDir = flag.String("base_dir", basedir.Find("golang.org/x/perf/storage/appengine"), "base `directory` for static files")
)
func main() {
flag.Parse()
db, err := db.OpenSQL("sqlite3", ":memory:")
if *baseDir == "" {
log.Print("base_dir is required and could not be automatically found")
flag.Usage()
}
db, err := db.OpenSQL("sqlite3", *dsn)
if err != nil {
log.Fatalf("open database: %v", err)
}
......@@ -34,6 +47,7 @@ func main() {
FS: fs,
ViewURLBase: *viewURLBase,
Auth: func(http.ResponseWriter, *http.Request) (string, error) { return "", nil },
BaseDir: *baseDir,
}
app.RegisterOnMux(http.DefaultServeMux)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment