aboutsummaryrefslogtreecommitdiffstats
path: root/tools/syz-testbed/table.go
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2021-11-25 17:55:58 +0000
committerAleksandr Nogikh <wp32pw@gmail.com>2021-12-06 14:29:36 +0100
commit9fca97cadd2e63056f40e69d80ca1e798b2a18e8 (patch)
tree6e6ae13406cd08aa46785cfdea86c6b00ae5465e /tools/syz-testbed/table.go
parent1ba1043eb4dc79008b7138f2fc04e136e2f796a3 (diff)
tools/syz-testbed: introduce a special type for table
This simplifies table generation and will let us more easily implement relative difference and p-value calculation and printing.
Diffstat (limited to 'tools/syz-testbed/table.go')
-rw-r--r--tools/syz-testbed/table.go121
1 files changed, 121 insertions, 0 deletions
diff --git a/tools/syz-testbed/table.go b/tools/syz-testbed/table.go
new file mode 100644
index 000000000..7e293b360
--- /dev/null
+++ b/tools/syz-testbed/table.go
@@ -0,0 +1,121 @@
+// Copyright 2021 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package main
+
+import (
+ "encoding/csv"
+ "fmt"
+ "math"
+ "os"
+ "sort"
+
+ "github.com/google/syzkaller/pkg/stats"
+)
+
+type Cell = interface{}
+
+// All tables that syz-testbed generates have named columns and rows.
+// Table type simplifies generation and processing of such tables.
+type Table struct {
+ TopLeftHeader string
+ ColumnHeaders []string
+ Cells map[string]map[string]Cell
+}
+
+type ValueCell struct {
+ Value float64
+ Sample *stats.Sample
+ ValueDiff *float64
+ PValue *float64
+}
+
+func NewValueCell(sample *stats.Sample) *ValueCell {
+ return &ValueCell{Value: sample.Median(), Sample: sample}
+}
+
+func (c *ValueCell) String() string {
+ const fractionCutoff = 100
+ if math.Abs(c.Value) < fractionCutoff {
+ return fmt.Sprintf("%.1f", c.Value)
+ }
+ return fmt.Sprintf("%.0f", math.Round(c.Value))
+}
+
+func NewTable(topLeft string, columns ...string) *Table {
+ return &Table{
+ TopLeftHeader: topLeft,
+ ColumnHeaders: columns,
+ }
+}
+
+func (t *Table) Get(row, column string) Cell {
+ if t.Cells == nil {
+ return nil
+ }
+ rowMap := t.Cells[row]
+ if rowMap == nil {
+ return nil
+ }
+ return rowMap[column]
+}
+
+func (t *Table) Set(row, column string, value Cell) {
+ if t.Cells == nil {
+ t.Cells = make(map[string]map[string]Cell)
+ }
+ rowMap, ok := t.Cells[row]
+ if !ok {
+ rowMap = make(map[string]Cell)
+ t.Cells[row] = rowMap
+ }
+ rowMap[column] = value
+}
+
+func (t *Table) AddColumn(column string) {
+ t.ColumnHeaders = append(t.ColumnHeaders, column)
+}
+
+func (t *Table) AddRow(row string, cells ...Cell) {
+ if len(cells) != len(t.ColumnHeaders) {
+ panic("AddRow: the length of the row does not equal the number of columns")
+ }
+ for i, col := range t.ColumnHeaders {
+ t.Set(row, col, cells[i])
+ }
+}
+
+func (t *Table) SortedRows() []string {
+ rows := []string{}
+ for key := range t.Cells {
+ rows = append(rows, key)
+ }
+ sort.Strings(rows)
+ return rows
+}
+
+func (t *Table) ToStrings() [][]string {
+ table := [][]string{}
+ headers := append([]string{t.TopLeftHeader}, t.ColumnHeaders...)
+ table = append(table, headers)
+ if t.Cells != nil {
+ rowHeaders := t.SortedRows()
+ for _, row := range rowHeaders {
+ tableRow := []string{row}
+ for _, column := range t.ColumnHeaders {
+ tableRow = append(tableRow, fmt.Sprintf("%s", t.Get(row, column)))
+ }
+ table = append(table, tableRow)
+ }
+ }
+ return table
+}
+
+func (t *Table) SaveAsCsv(fileName string) error {
+ f, err := os.Create(fileName)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ return csv.NewWriter(f).WriteAll(t.ToStrings())
+}