Commit f01f51ba authored by Quentin Smith's avatar Quentin Smith

analysis/app: group benchstat results if a name label is in the query

Change-Id: Ia8761c7709e09196dbb2499bcec76ab5bfc0c715
Reviewed-on: https://go-review.googlesource.com/35949Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent dfb98451
......@@ -6,14 +6,18 @@ package app
import (
"bytes"
"fmt"
"html/template"
"io/ioutil"
"net/http"
"sort"
"strconv"
"strings"
"unicode"
"golang.org/x/perf/analysis/internal/benchstat"
"golang.org/x/perf/storage/benchfmt"
"golang.org/x/perf/storage/query"
)
// A resultGroup holds a list of results and tracks the distinct labels found in that list.
......@@ -165,6 +169,56 @@ type compareData struct {
CommonLabels benchfmt.Labels
}
// queryKeys returns the keys that are exact-matched by q.
func queryKeys(q string) map[string]bool {
out := make(map[string]bool)
for _, part := range query.SplitWords(q) {
// TODO(quentin): This func is shared with db.go; refactor?
i := strings.IndexFunc(part, func(r rune) bool {
return r == ':' || r == '>' || r == '<' || unicode.IsSpace(r) || unicode.IsUpper(r)
})
if i >= 0 && part[i] == ':' {
out[part[:i]] = true
}
}
return out
}
// elideKeyValues returns content, a benchmark format line, with the
// values of any keys in keys elided.
func elideKeyValues(content string, keys map[string]bool) string {
var end string
if i := strings.IndexFunc(content, unicode.IsSpace); i >= 0 {
content, end = content[:i], content[i:]
}
// Check for gomaxprocs value
if i := strings.LastIndex(content, "-"); i >= 0 {
_, err := strconv.Atoi(content[i+1:])
if err == nil {
if keys["gomaxprocs"] {
content, end = content[:i], "-*"+end
} else {
content, end = content[:i], content[i:]+end
}
}
}
parts := strings.Split(content, "/")
for i, part := range parts {
if equals := strings.Index(part, "="); equals >= 0 {
if keys[part[:equals]] {
parts[i] = part[:equals] + "=*"
}
} else if i == 0 {
if keys["name"] {
parts[i] = "Benchmark*"
}
} else if keys[fmt.Sprintf("sub%d", i)] {
parts[i] = "*"
}
}
return strings.Join(parts, "/") + end
}
func (a *App) compareQuery(q string) *compareData {
// Parse query
prefix, queries := parseQueryString(q)
......@@ -174,13 +228,16 @@ func (a *App) compareQuery(q string) *compareData {
var groups []*resultGroup
var found int
for _, qPart := range queries {
keys := queryKeys(qPart)
group := &resultGroup{}
if prefix != "" {
qPart = prefix + " " + qPart
}
res := a.StorageClient.Query(qPart)
for res.Next() {
group.add(res.Result())
result := res.Result()
result.Content = elideKeyValues(result.Content, keys)
group.add(result)
found++
}
err := res.Err()
......
......@@ -131,3 +131,27 @@ func TestAddToQuery(t *testing.T) {
})
}
}
func TestElideKeyValues(t *testing.T) {
type sb map[string]bool
tests := []struct {
content string
keys sb
want string
}{
{"BenchmarkOne/key=1-1 1 ns/op", sb{"key": true}, "BenchmarkOne/key=*-1 1 ns/op"},
{"BenchmarkOne/key=1-2 1 ns/op", sb{"other": true}, "BenchmarkOne/key=1-2 1 ns/op"},
{"BenchmarkOne/key=1/key2=2-3 1 ns/op", sb{"key": true}, "BenchmarkOne/key=*/key2=2-3 1 ns/op"},
{"BenchmarkOne/foo/bar-4 1 ns/op", sb{"sub1": true}, "BenchmarkOne/*/bar-4 1 ns/op"},
{"BenchmarkOne/foo/bar-5 1 ns/op", sb{"gomaxprocs": true}, "BenchmarkOne/foo/bar-* 1 ns/op"},
{"BenchmarkOne/foo/bar-6 1 ns/op", sb{"name": true}, "Benchmark*/foo/bar-6 1 ns/op"},
}
for i, test := range tests {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
have := elideKeyValues(test.content, test.keys)
if have != test.want {
t.Errorf("elideKeys(%q, %#v) = %q, want %q", test.content, map[string]bool(test.keys), have, test.want)
}
})
}
}
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