aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2021-11-30 14:30:41 +0000
committerAleksandr Nogikh <wp32pw@gmail.com>2021-12-06 14:29:36 +0100
commit45bf1e232e9518f6666c051c242a4ef656a97228 (patch)
treeef77af18a08a6d4e8eade22466ebdcaad813814e
parent9bc82119f890ecaa556935301dc721f90100d2dd (diff)
tools/syz-testbed: align table per particular rows
It turns out that we often want to see the data aligned on some specific property - e.g. align all checkouts by "exec total" and see how other parameters differ. Add a preliminary support of such a feature. On a row title click, pick the minimal value in the row and wind the history of each column back until the target row value is closest to the minimal one.
-rw-r--r--tools/syz-testbed/html.go37
-rw-r--r--tools/syz-testbed/stats.go33
2 files changed, 61 insertions, 9 deletions
diff --git a/tools/syz-testbed/html.go b/tools/syz-testbed/html.go
index 396265dc5..bae718fda 100644
--- a/tools/syz-testbed/html.go
+++ b/tools/syz-testbed/html.go
@@ -126,6 +126,7 @@ type uiTable struct {
ColumnURL func(string) string
RowURL func(string) string
Extra bool
+ AlignedBy string
}
type uiStatView struct {
@@ -153,7 +154,12 @@ func (ctx *TestbedContext) httpMain(w http.ResponseWriter, r *http.Request) {
return
}
uiView := uiStatView{Name: activeView.Name}
- table, err := activeView.StatsTable()
+
+ alignBy := r.FormValue("align")
+ if alignBy == "" {
+ alignBy = "fuzzing"
+ }
+ table, err := activeView.AlignedStatsTable(alignBy)
if err != nil {
log.Printf("stat table generation failed: %s", err)
} else {
@@ -172,8 +178,23 @@ func (ctx *TestbedContext) httpMain(w http.ResponseWriter, r *http.Request) {
if column == baseColumn {
return ""
}
- return "/?view=" + url.QueryEscape(activeView.Name) + "&base_column=" + url.QueryEscape(column)
+ v := url.Values{}
+ v.Set("view", activeView.Name)
+ v.Set("base_column", column)
+ v.Set("align", alignBy)
+ return "/?" + v.Encode()
+ },
+ RowURL: func(row string) string {
+ if row == alignBy {
+ return ""
+ }
+ v := url.Values{}
+ v.Set("view", activeView.Name)
+ v.Set("base_column", baseColumn)
+ v.Set("align", row)
+ return "/?" + v.Encode()
},
+ AlignedBy: alignBy,
}
}
@@ -270,6 +291,9 @@ href="?view={{$view.Name}}">█ {{$view.Name}}</a>
{{define "PrintTable"}}
{{$uiTable := .}}
{{if .Table}}
+{{if $uiTable.AlignedBy}}
+ The data are aligned by {{$uiTable.AlignedBy}} <br />
+{{end}}
<table class="list_table">
<tr>
<th>{{.Table.TopLeftHeader}}</th>
@@ -289,7 +313,14 @@ href="?view={{$view.Name}}">█ {{$view.Name}}</a>
</tr>
{{range $r := .Table.SortedRows}}
<tr>
- <td>{{$r}}</td>
+ <td>
+ {{$url := ""}}
+ {{if $uiTable.RowURL}}{{$url = (call $uiTable.RowURL $r)}}{{end}}
+ {{if $url}}<a href="{{$url}}">{{$r}}</a>
+ {{else}}
+ {{$r}}
+ {{end}}
+ </td>
{{range $c := $uiTable.Table.ColumnHeaders}}
{{$cell := ($uiTable.Table.Get $r $c)}}
{{if and $cell $uiTable.Extra}}
diff --git a/tools/syz-testbed/stats.go b/tools/syz-testbed/stats.go
index 42ce9537b..20ad9bb30 100644
--- a/tools/syz-testbed/stats.go
+++ b/tools/syz-testbed/stats.go
@@ -182,26 +182,47 @@ func (group RunResultGroup) groupNthRecord(i int) map[string]*stats.Sample {
}
func (view StatView) StatsTable() (*Table, error) {
- commonLen := 0
+ return view.AlignedStatsTable("uptime")
+}
+
+func (view StatView) AlignedStatsTable(field string) (*Table, error) {
+ // We assume that the stats values are nonnegative.
+ var commonValue float64
for _, group := range view.Groups {
minLen := group.minResultLength()
if minLen == 0 {
continue
}
- if minLen < commonLen || commonLen == 0 {
- commonLen = minLen
+ sampleGroup := group.groupNthRecord(minLen - 1)
+ sample, ok := sampleGroup[field]
+ if !ok {
+ return nil, fmt.Errorf("field %v is not found", field)
+ }
+ currValue := sample.Median()
+ if currValue < commonValue || commonValue == 0 {
+ commonValue = currValue
}
}
- // Map: stats key x group name -> value.
table := NewTable("Property")
cells := make(map[string]map[string]string)
for _, group := range view.Groups {
table.AddColumn(group.Name)
- if group.minResultLength() == 0 {
+ minLen := group.minResultLength()
+ if minLen == 0 {
// Skip empty groups.
continue
}
- samples := group.groupNthRecord(commonLen - 1)
+ // Unwind the samples so that they are aligned on the field value.
+ var samples map[string]*stats.Sample
+ for i := minLen - 1; i >= 0; i-- {
+ candidate := group.groupNthRecord(i)
+ // TODO: consider data interpolation.
+ if candidate[field].Median() >= commonValue {
+ samples = candidate
+ } else {
+ break
+ }
+ }
for key, sample := range samples {
if _, ok := cells[key]; !ok {
cells[key] = make(map[string]string)