[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH RFC 29/59] report: Add basic html report



From: George Dunlap <george.dunlap@xxxxxxxxxx>

Use Google's chartapi to create a (mostly) self-contained html file.
Start with just scatterplots of the raw data for proof-of-concept.

Signed-off-by: George Dunlap <george.dunlap@xxxxxxxxxx>
---
 Makefile      |   2 +-
 benchmark.go  |   1 +
 htmlreport.go | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 main.go       |  12 +++
 4 files changed, 247 insertions(+), 1 deletion(-)
 create mode 100644 htmlreport.go

diff --git a/Makefile b/Makefile
index 54f2ce8..c1b9ee4 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@ CGO_LIBS = -lyajl -lxenlight
 XENLIB_PATH ?= /build/hg/xen.git/dist/install/usr/local/lib/
 CGO_LDFLAGS = -L$(XENLIB_PATH) -Wl,-rpath-link=$(XENLIB_PATH) $(CGO_LIBS)
 
-schedbench: main.go processworker.go xenworker.go benchmark.go run.go libxl.go
+schedbench: main.go processworker.go xenworker.go benchmark.go run.go libxl.go 
htmlreport.go
        CGO_LDFLAGS="$(CGO_LDFLAGS)" CGO_CFLAGS="$(CGO_CFLAGS)" go build -o $@ 
$^
 
 .PHONY: clean
diff --git a/benchmark.go b/benchmark.go
index de1f650..8be00a0 100644
--- a/benchmark.go
+++ b/benchmark.go
@@ -438,3 +438,4 @@ func (plan *BenchmarkPlan) TextReport(level int) (err 
error) {
 
        return
 }
+
diff --git a/htmlreport.go b/htmlreport.go
new file mode 100644
index 0000000..6f61998
--- /dev/null
+++ b/htmlreport.go
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2016 George W. Dunlap, Citrix Systems UK Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+package main
+
+import (
+       "fmt"
+       "os"
+       "io"
+       "encoding/json"
+)
+
+type OptionAxis struct {
+       Title string `json:"title"`
+       MinValue float64 `json:"minValue"`
+       MaxValue float64 `json:"maxValue"`
+}
+
+type Options struct {
+       Title string     `json:"title"`
+       HAxis OptionAxis `json:"hAxis"`
+       VAxis OptionAxis `json:"vAxis"`
+       Legend string    `json:"legend"`
+}
+
+type Point struct {
+       x float64
+       y float64
+}
+
+type RunRaw struct {
+       Label string
+       Points [][]Point
+}
+
+func (options *Options) OutputJavascript(w io.Writer, id int) (err error) {
+       var optionsJson []byte
+       optionsJson, err = json.Marshal(options)
+       if err != nil {
+               return
+       }
+
+       fmt.Fprintf(w, "        var sp%dopt = ", id)
+       fmt.Fprint(w, string(optionsJson))
+       fmt.Fprintln(w, ";")
+
+       return
+}
+
+func (p *Point) OutputJson(w io.Writer, id int, max int) (err error) {
+       fmt.Fprintf(w, "            [%f", p.x)
+       for i := 0; i < max; i++ {
+               if i == id {
+                       fmt.Fprintf(w, ", %f", p.y)
+               } else {
+                       fmt.Fprintf(w, ", null")
+               }
+       }
+       fmt.Fprint(w, "],\n")
+       return
+}
+
+func (d *RunRaw) OutputHTML(w io.Writer, run int) (err error) {
+       fmt.Fprintf(w, "    <div class='scatterplot' 
id='scatterplot%d'></div>\n", run)
+       return
+}
+
+func (d *RunRaw) OutputJavascript(w io.Writer, run int) (err error) {
+       var options Options
+
+       options.Title = fmt.Sprintf("Run %s (%d) Individual Throughput", 
d.Label, run)
+       options.HAxis.Title = "Time"
+       options.VAxis.Title = "Throughput"
+
+       var xmm MinMax
+       var ymm MinMax
+       for i := range d.Points {
+               for j := range d.Points[i] {
+                       xmm.Update(d.Points[i][j].x)
+                       ymm.Update(d.Points[i][j].y)
+               }
+       }
+
+       options.HAxis.MaxValue = xmm.Max
+       options.VAxis.MaxValue = ymm.Max
+
+       err = options.OutputJavascript(w, run)
+       if err != nil {
+               return
+       }
+
+       fmt.Printf("        var sp%ddata = new 
google.visualization.DataTable();\n", run)
+       fmt.Printf("        sp%ddata.addColumn('number', 'Time');\n", run)
+       for i := range d.Points {
+               fmt.Printf("        sp%ddata.addColumn('number', 'Worker 
%d');\n", run, i)
+       }
+       fmt.Printf("        sp%ddata.addRows([\n", run)
+
+       // Can't use json here because we need to be able to use 'null' for 
non-existent values
+       for i := range d.Points {
+               for j := range d.Points[i] {
+                       err = d.Points[i][j].OutputJson(w, i, len(d.Points))
+                       if err != nil {
+                               return
+                       }
+               }
+       }
+       fmt.Print("          ]);\n")
+       
+       fmt.Printf("        var sp%dchart = new 
google.visualization.ScatterChart(document.getElementById('scatterplot%d'));\n",
 run, run);
+       fmt.Printf("        sp%dchart.draw(sp%ddata, sp%dopt);\n\n", run, run, 
run)
+       
+       return
+}
+
+type HTMLReport struct {
+       Raw []RunRaw
+}
+
+
+func (rpt *HTMLReport) Output(w io.Writer) (err error) {
+       // Print start -> json charts
+       fmt.Fprint(w,
+               `<html>
+  <head>
+    <style>
+      .scatterplot {
+      margin:auto;
+      width: 100vw;
+      height: 60vw;
+      }
+
+      .empty {
+      margin: auto;
+      }
+    </style>
+    <script type="text/javascript" 
src="https://www.gstatic.com/charts/loader.js";></script>
+    <script type="text/javascript">
+      google.charts.load('current', {'packages':['corechart']});
+      google.charts.setOnLoadCallback(drawCharts);
+      function drawCharts() {
+`);
+       // Print json chart code
+       for i := range rpt.Raw {
+               err = rpt.Raw[i].OutputJavascript(w, i)
+               if err != nil {
+                       return
+               }
+       }
+       // Print json -> html
+       fmt.Fprint(w,
+               `      }
+    </script>
+  </head>
+  <body>
+`);
+       // Print html
+       for i := range rpt.Raw {
+               err = rpt.Raw[i].OutputHTML(w, i)
+               if err != nil {
+                       return
+               }
+       }
+       // Print html -> end
+       fmt.Fprint(w,
+               `  </body>
+</html>
+`);
+       return
+}
+
+func (rpt *HTMLReport) AddRun(run *BenchmarkRun) (err error) {
+       var d RunRaw
+
+       d.Label = run.Label
+       for set := range run.Results.Summary {
+               var idPoints []Point
+               for id := range run.Results.Summary[set].Workers {
+                       var le WorkerReport
+                       for _, e := range 
run.Results.Summary[set].Workers[id].Raw {
+                               if e.Now > le.Now {
+                                       time := float64(e.Now) / SEC
+                                       tput := Throughput(e.Now, e.Kops, 
le.Now, le.Kops)
+                                       idPoints = append(idPoints, 
Point{x:time, y:tput})
+                               }
+                               le = e
+                       }
+               }
+               d.Points = append(d.Points, idPoints)
+       }
+       rpt.Raw = append(rpt.Raw, d)
+       return
+}
+
+func (plan *BenchmarkPlan) HTMLReport() (err error) {
+       rpt := HTMLReport{}
+
+       for i := range plan.Runs {
+               r := &plan.Runs[i]
+               if ! r.Completed {
+                       fmt.Printf("Test [%d] %s not run\n", i, r.Label)
+               }
+
+               err = r.Process()
+               if err != nil {
+                       fmt.Printf("Error processing [%d] %s: %v\n", i, 
r.Label, err)
+                       return
+               }
+
+               err = rpt.AddRun(r)
+               if err != nil {
+                       return
+               }
+       }
+       err = rpt.Output(os.Stdout)
+
+       return
+}
diff --git a/main.go b/main.go
index 13230a7..f8d77cf 100644
--- a/main.go
+++ b/main.go
@@ -107,6 +107,18 @@ func main() {
                        fmt.Println("Running benchmark run:", err)
                        os.Exit(1)
                }
+       case "htmlreport":
+               plan, err := LoadBenchmark(filename)
+               if err != nil {
+                       fmt.Println("Loading benchmark ", filename, " ", err)
+                       os.Exit(1)
+               }
+       
+               err = plan.HTMLReport()
+               if err != nil {
+                       fmt.Println("Running benchmark run:", err)
+                       os.Exit(1)
+               }
        default:
                fmt.Println("Unknown argument: ", os.Args[1])
        }
-- 
2.7.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.