From 45bf1e232e9518f6666c051c242a4ef656a97228 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Tue, 30 Nov 2021 14:30:41 +0000 Subject: 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. --- tools/syz-testbed/html.go | 37 ++++++++++++++++++++++++++++++++++--- tools/syz-testbed/stats.go | 33 +++++++++++++++++++++++++++------ 2 files changed, 61 insertions(+), 9 deletions(-) (limited to 'tools') 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}} {{define "PrintTable"}} {{$uiTable := .}} {{if .Table}} +{{if $uiTable.AlignedBy}} + The data are aligned by {{$uiTable.AlignedBy}}
+{{end}} @@ -289,7 +313,14 @@ href="?view={{$view.Name}}">█ {{$view.Name}} {{range $r := .Table.SortedRows}} - + {{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) -- cgit mrf-deployment
{{.Table.TopLeftHeader}}
{{$r}} + {{$url := ""}} + {{if $uiTable.RowURL}}{{$url = (call $uiTable.RowURL $r)}}{{end}} + {{if $url}}{{$r}} + {{else}} + {{$r}} + {{end}} +