aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/trace
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2017-07-24 11:37:59 -0400
committerAustin Clements <austin@google.com>2018-11-05 19:10:04 +0000
commit3053788cac0343c2fd29806ebc358d2f63976695 (patch)
tree4448e0cc96b61734eaa7857f437f5d27ce1ca048 /src/cmd/trace
parentf999576dd8df6e4d09e3c67c23ba4d8dc18d53d3 (diff)
downloadgo-3053788cac0343c2fd29806ebc358d2f63976695.tar.gz
go-3053788cac0343c2fd29806ebc358d2f63976695.zip
cmd/trace: add minimum mutator utilization (MMU) plot
This adds an endpoint to the trace tool that plots the minimum mutator utilization curve using information on mark assists and GC pauses from the trace. This commit implements a fairly straightforward O(nm) algorithm for computing the MMU (and tests against an even more direct but slower algorithm). Future commits will extend and optimize this algorithm. This should be useful for debugging and understanding mutator utilization issues like #14951, #14812, #18155. #18534, #21107, particularly once follow-up CLs add trace cross-referencing. Change-Id: Ic2866869e7da1e6c56ba3e809abbcb2eb9c4923a Reviewed-on: https://go-review.googlesource.com/c/60790 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Diffstat (limited to 'src/cmd/trace')
-rw-r--r--src/cmd/trace/main.go1
-rw-r--r--src/cmd/trace/mmu.go160
2 files changed, 161 insertions, 0 deletions
diff --git a/src/cmd/trace/main.go b/src/cmd/trace/main.go
index a33d2f4679..f6ec38d673 100644
--- a/src/cmd/trace/main.go
+++ b/src/cmd/trace/main.go
@@ -202,6 +202,7 @@ var templMain = template.Must(template.New("").Parse(`
<a href="/sched">Scheduler latency profile</a> (<a href="/sche?raw=1" download="sched.profile">⬇</a>)<br>
<a href="/usertasks">User-defined tasks</a><br>
<a href="/userregions">User-defined regions</a><br>
+<a href="/mmu">Minimum mutator utilization</a><br>
</body>
</html>
`))
diff --git a/src/cmd/trace/mmu.go b/src/cmd/trace/mmu.go
new file mode 100644
index 0000000000..cc14025d38
--- /dev/null
+++ b/src/cmd/trace/mmu.go
@@ -0,0 +1,160 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Minimum mutator utilization (MMU) graphing.
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ trace "internal/traceparser"
+ "log"
+ "math"
+ "net/http"
+ "strings"
+ "sync"
+ "time"
+)
+
+func init() {
+ http.HandleFunc("/mmu", httpMMU)
+ http.HandleFunc("/mmuPlot", httpMMUPlot)
+}
+
+var mmuCache struct {
+ init sync.Once
+ util []trace.MutatorUtil
+ mmuCurve *trace.MMUCurve
+ err error
+}
+
+func getMMUCurve() ([]trace.MutatorUtil, *trace.MMUCurve, error) {
+ mmuCache.init.Do(func() {
+ tr, err := parseTrace()
+ if err != nil {
+ mmuCache.err = err
+ } else {
+ mmuCache.util = tr.MutatorUtilization()
+ mmuCache.mmuCurve = trace.NewMMUCurve(mmuCache.util)
+ }
+ })
+ return mmuCache.util, mmuCache.mmuCurve, mmuCache.err
+}
+
+// httpMMU serves the MMU plot page.
+func httpMMU(w http.ResponseWriter, r *http.Request) {
+ http.ServeContent(w, r, "", time.Time{}, strings.NewReader(templMMU))
+}
+
+// httpMMUPlot serves the JSON data for the MMU plot.
+func httpMMUPlot(w http.ResponseWriter, r *http.Request) {
+ mu, mmuCurve, err := getMMUCurve()
+ if err != nil {
+ http.Error(w, fmt.Sprintf("failed to parse events: %v", err), http.StatusInternalServerError)
+ return
+ }
+
+ // Find a nice starting point for the plot.
+ xMin := time.Second
+ for xMin > 1 {
+ if mmu := mmuCurve.MMU(xMin); mmu < 0.0001 {
+ break
+ }
+ xMin /= 1000
+ }
+ // Cover six orders of magnitude.
+ xMax := xMin * 1e6
+ // But no more than the length of the trace.
+ if maxMax := time.Duration(mu[len(mu)-1].Time - mu[0].Time); xMax > maxMax {
+ xMax = maxMax
+ }
+ // Compute MMU curve.
+ logMin, logMax := math.Log(float64(xMin)), math.Log(float64(xMax))
+ const samples = 100
+ plot := make([][2]float64, samples)
+ for i := 0; i < samples; i++ {
+ window := time.Duration(math.Exp(float64(i)/(samples-1)*(logMax-logMin) + logMin))
+ y := mmuCurve.MMU(window)
+ plot[i] = [2]float64{float64(window), y}
+ }
+
+ // Create JSON response.
+ err = json.NewEncoder(w).Encode(map[string]interface{}{"xMin": int64(xMin), "xMax": int64(xMax), "curve": plot})
+ if err != nil {
+ log.Printf("failed to serialize response: %v", err)
+ return
+ }
+}
+
+var templMMU = `<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
+ <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
+ <script type="text/javascript">
+ google.charts.load('current', {'packages':['corechart']});
+ google.charts.setOnLoadCallback(refreshChart);
+
+ function niceDuration(ns) {
+ if (ns < 1e3) { return ns + 'ns'; }
+ else if (ns < 1e6) { return ns / 1e3 + 'µs'; }
+ else if (ns < 1e9) { return ns / 1e6 + 'ms'; }
+ else { return ns / 1e9 + 's'; }
+ }
+
+ function refreshChart() {
+ $.getJSON('/mmuPlot')
+ .fail(function(xhr, status, error) {
+ alert('failed to load plot: ' + status);
+ })
+ .done(drawChart);
+ }
+
+ function drawChart(plotData) {
+ var curve = plotData.curve;
+ var data = new google.visualization.DataTable();
+ data.addColumn('number', 'Window duration');
+ data.addColumn('number', 'Minimum mutator utilization');
+ data.addRows(curve);
+ for (var i = 0; i < curve.length; i++) {
+ data.setFormattedValue(i, 0, niceDuration(curve[i][0]));
+ }
+
+ var options = {
+ chart: {
+ title: 'Minimum mutator utilization',
+ },
+ hAxis: {
+ title: 'Window duration',
+ scaleType: 'log',
+ ticks: [],
+ },
+ vAxis: {
+ title: 'Minimum mutator utilization',
+ minValue: 0.0,
+ maxValue: 1.0,
+ },
+ legend: { position: 'none' },
+ width: 900,
+ height: 500,
+ chartArea: { width: '80%', height: '80%' },
+ };
+ for (var v = plotData.xMin; v <= plotData.xMax; v *= 10) {
+ options.hAxis.ticks.push({v:v, f:niceDuration(v)});
+ }
+
+ var container = $('#mmu_chart');
+ container.empty();
+ var chart = new google.visualization.LineChart(container[0]);
+ chart.draw(data, options);
+ }
+ </script>
+ </head>
+ <body>
+ <div id="mmu_chart" style="width: 900px; height: 500px">Loading plot...</div>
+ </body>
+</html>
+`