xen-devel.lists.xenproject.org archive mirror
 help / color / mirror / Atom feed
From: Ronald Rojas <ronladred@gmail.com>
To: xen-devel <xen-devel@lists.xen.org>,
	Ian Jackson <ian.jackson@eu.citrix.com>,
	Wei Liu <wei.liu2@citrix.com>,
	George Dunlap <george.dunlap@citrix.com>,
	George Dunlap <dunlapg@umich.edu>
Subject: [PATCH RFC 29/59] report: Add basic html report
Date: Wed, 28 Dec 2016 20:14:22 -0500	[thread overview]
Message-ID: <1482974092-15891-29-git-send-email-ronladred@gmail.com> (raw)
In-Reply-To: <1482974092-15891-1-git-send-email-ronladred@gmail.com>

From: George Dunlap <george.dunlap@citrix.com>

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@citrix.com>
---
 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@lists.xen.org
https://lists.xen.org/xen-devel

  parent reply	other threads:[~2016-12-29  1:14 UTC|newest]

Thread overview: 67+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-12-29  1:13 [PATCH RFC 01/59] Initial controller framework Ronald Rojas
2016-12-29  1:13 ` [PATCH RFC 02/59] controller: Revamp communication structure Ronald Rojas
2016-12-29  1:13 ` [PATCH RFC 03/59] controller: Initial attempt to generalize process / vm creation Ronald Rojas
2016-12-29  1:13 ` [PATCH RFC 04/59] Controller: Move process worker into its own file Ronald Rojas
2016-12-29  1:13 ` [PATCH RFC 05/59] controller: Add WorkerParams argument to Init in Worker interface Ronald Rojas
2016-12-29  1:13 ` [PATCH RFC 06/59] Reorganize to enable "Dist" directory Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 07/59] controller: Introduce basic Xen functionality Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 08/59] controller: Exit after second SIGINT Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 09/59] controller: Refactor creation and stopping of workers into WorkerList methods Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 10/59] controller: First cut at BenchmarkParams Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 11/59] Refactor to move towards benchmark "plans" and data analysis Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 12/59] Basic 'report' functionality Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 13/59] Add GPL headers / COPYING file (v2 only) Ronald Rojas
2016-12-29 10:51   ` Wei Liu
2016-12-29  1:14 ` [PATCH RFC 14/59] benchmark: Store data in terms of worker sets and worker ids Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 15/59] controller: Move "running" code to a separate file Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 16/59] controller: Rename an element in BenchmarkRun to be more accurate Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 17/59] controller: Collect and display statistics on WorkerSets Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 18/59] controller: Add cpupool global config Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 19/59] Add basic libxl framework, get domain cpu_time Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 20/59] xenworker: Use libxl_domain_unpause rather than forking xl Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 21/59] Report utilization statistics Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 22/59] Use tsc for time rather than rumpkernel clock_gettime() Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 23/59] run: Don't collect results reported after command to stop guests is issued Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 24/59] report: Lots of changes Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 25/59] main: Change default workload to something a bit more extreme Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 26/59] Use kops rather than mops Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 27/59] report: Allow report verbosity to be specified Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 28/59] controller: Handle worker early death Ronald Rojas
2016-12-29  1:14 ` Ronald Rojas [this message]
2016-12-29  1:14 ` [PATCH RFC 30/59] htmlreport: Include utilization scatterplots Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 31/59] Make a binary that can run reports on a system without libxl Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 32/59] controller: Allow specification of an input file Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 33/59] controller: Add verbosity argument and update README with new instructions Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 34/59] controller: Make a useful config file Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 35/59] libxl: Add ListCpupool Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 36/59] controller: Make 'dummy' at the level of 'run' rather than xenworker Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 37/59] libxl.go: Provide a single global context by default Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 38/59] controller: Allow multiple schedulers in the same benchmark file Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 39/59] libxl.go: Put common link flags in libxl.go Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 40/59] controller: Add / update GPL text Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 41/59] libxl.go: Link statically rather than dynamically Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 42/59] plan: Allow "templating" from other runs Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 43/59] libxl: Add bitmap support Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 44/59] libxl: Implement CpupoolCreate Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 45/59] libxl: Implement Destroy, Add/Remove operations Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 46/59] libxl: Reorganize bitmapGotoC Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 47/59] libxl: Reorganize code Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 48/59] libxl: Add Ctx.CheckOpen Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 49/59] libxl: Implement libxl_cpupool_info and Scheduler.FromString() Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 50/59] libxl: Fix Bitmap.Max(), make Test() / Clear() more robust Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 51/59] controller: Make and/or modify cpupools when possible Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 52/59] libxl: Implement Bitmap.String() Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 53/59] controller: Add WorkerConfig.SoftAffinity Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 54/59] controller/run: Add RunConfig.NumaDisable Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 55/59] plan: Make the matrix generation more programmatic Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 56/59] controller/plan: Add NumaDisable to SimpleMatrix Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 57/59] tools/blktap2: remove unused inclusion of sys/sysctl.l Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 58/59] remove irrelevant files from old repository Ronald Rojas
2016-12-29  1:14 ` [PATCH RFC 59/59] tools/xenlight: Create interface for xenlight info Ronald Rojas
2016-12-29 10:34   ` George Dunlap
2016-12-29 10:52     ` Wei Liu
2016-12-29 13:49       ` Ronald Rojas
2016-12-29 13:45   ` George Dunlap
2016-12-29 20:20     ` George Dunlap
2017-01-03 17:45     ` Ronald Rojas
2017-01-04 16:44       ` George Dunlap

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1482974092-15891-29-git-send-email-ronladred@gmail.com \
    --to=ronladred@gmail.com \
    --cc=dunlapg@umich.edu \
    --cc=george.dunlap@citrix.com \
    --cc=ian.jackson@eu.citrix.com \
    --cc=wei.liu2@citrix.com \
    --cc=xen-devel@lists.xen.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).