[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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |